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.hardware.biometrics.BiometricConstants.BIOMETRIC_ERROR_CANCELED;
20 import static android.hardware.biometrics.BiometricConstants.BIOMETRIC_SUCCESS;
21 
22 import static com.google.common.truth.Truth.assertThat;
23 
24 import static junit.framework.Assert.assertTrue;
25 import static junit.framework.Assert.fail;
26 
27 import static org.junit.Assert.assertEquals;
28 import static org.junit.Assert.assertFalse;
29 import static org.junit.Assert.assertNotEquals;
30 import static org.junit.Assert.assertNotNull;
31 import static org.junit.Assert.assertNull;
32 import static org.mockito.ArgumentMatchers.any;
33 import static org.mockito.ArgumentMatchers.anyBoolean;
34 import static org.mockito.ArgumentMatchers.anyInt;
35 import static org.mockito.ArgumentMatchers.eq;
36 import static org.mockito.Mockito.mock;
37 import static org.mockito.Mockito.never;
38 import static org.mockito.Mockito.verify;
39 import static org.mockito.Mockito.when;
40 
41 import android.content.Context;
42 import android.hardware.biometrics.AuthenticateOptions;
43 import android.hardware.biometrics.BiometricAuthenticator;
44 import android.hardware.biometrics.BiometricConstants;
45 import android.hardware.biometrics.BiometricsProtoEnums;
46 import android.hardware.biometrics.IBiometricService;
47 import android.hardware.fingerprint.Fingerprint;
48 import android.os.Binder;
49 import android.os.Handler;
50 import android.os.IBinder;
51 import android.os.RemoteException;
52 import android.platform.test.annotations.Presubmit;
53 import android.testing.AndroidTestingRunner;
54 import android.testing.TestableContext;
55 import android.testing.TestableLooper;
56 import android.util.Slog;
57 
58 import androidx.annotation.NonNull;
59 import androidx.annotation.Nullable;
60 import androidx.test.InstrumentationRegistry;
61 import androidx.test.filters.SmallTest;
62 
63 import com.android.server.biometrics.log.BiometricContext;
64 import com.android.server.biometrics.log.BiometricLogger;
65 import com.android.server.biometrics.nano.BiometricSchedulerProto;
66 import com.android.server.biometrics.nano.BiometricsProto;
67 import com.android.server.biometrics.sensors.fingerprint.FingerprintUtils;
68 import com.android.server.biometrics.sensors.fingerprint.aidl.AidlSession;
69 
70 import org.junit.Before;
71 import org.junit.Rule;
72 import org.junit.Test;
73 import org.junit.runner.RunWith;
74 import org.mockito.Mock;
75 import org.mockito.MockitoAnnotations;
76 
77 import java.util.ArrayList;
78 import java.util.HashMap;
79 import java.util.List;
80 import java.util.Map;
81 import java.util.function.Supplier;
82 
83 @Presubmit
84 @SmallTest
85 @RunWith(AndroidTestingRunner.class)
86 @TestableLooper.RunWithLooper(setAsMainLooper = true)
87 public class BiometricSchedulerTest {
88 
89     private static final String TAG = "BiometricSchedulerTest";
90     private static final int TEST_SENSOR_ID = 1;
91     private static final int LOG_NUM_RECENT_OPERATIONS = 2;
92     private static final Fingerprint TEST_FINGERPRINT = new Fingerprint("" /* name */,
93             1 /* fingerId */, TEST_SENSOR_ID);
94 
95     @Rule
96     public final TestableContext mContext = new TestableContext(
97             InstrumentationRegistry.getContext(), null);
98     private BiometricScheduler mScheduler;
99     private IBinder mToken;
100     @Mock
101     private IBiometricService mBiometricService;
102     @Mock
103     private BiometricContext mBiometricContext;
104     @Mock
105     private AuthSessionCoordinator mAuthSessionCoordinator;
106 
107     @Before
setUp()108     public void setUp() {
109         MockitoAnnotations.initMocks(this);
110         mToken = new Binder();
111         when(mAuthSessionCoordinator.getLockoutStateFor(anyInt(), anyInt())).thenReturn(
112                 BIOMETRIC_SUCCESS);
113         when(mBiometricContext.getAuthSessionCoordinator()).thenReturn(mAuthSessionCoordinator);
114         mScheduler = new BiometricScheduler(TAG, new Handler(TestableLooper.get(this).getLooper()),
115                 BiometricScheduler.SENSOR_TYPE_UNKNOWN, null /* gestureAvailabilityTracker */,
116                 mBiometricService, LOG_NUM_RECENT_OPERATIONS);
117     }
118 
119     @Test
testClientDuplicateFinish_ignoredBySchedulerAndDoesNotCrash()120     public void testClientDuplicateFinish_ignoredBySchedulerAndDoesNotCrash() {
121         final Supplier<Object> nonNullDaemon = () -> mock(Object.class);
122 
123         final HalClientMonitor<Object> client1 = new TestHalClientMonitor(mContext, mToken,
124                 nonNullDaemon);
125         final HalClientMonitor<Object> client2 = new TestHalClientMonitor(mContext, mToken,
126                 nonNullDaemon);
127         mScheduler.scheduleClientMonitor(client1);
128         mScheduler.scheduleClientMonitor(client2);
129 
130         client1.mCallback.onClientFinished(client1, true /* success */);
131         client1.mCallback.onClientFinished(client1, true /* success */);
132     }
133 
134     @Test
testRemovesPendingOperations_whenNullHal_andNotBiometricPrompt()135     public void testRemovesPendingOperations_whenNullHal_andNotBiometricPrompt() {
136         // Even if second client has a non-null daemon, it needs to be canceled.
137         final TestHalClientMonitor client1 = new TestHalClientMonitor(mContext, mToken, () -> null);
138         final TestHalClientMonitor client2 = new TestHalClientMonitor(mContext, mToken,
139                 () -> mock(Object.class));
140 
141         final ClientMonitorCallback callback1 = mock(ClientMonitorCallback.class);
142         final ClientMonitorCallback callback2 = mock(ClientMonitorCallback.class);
143 
144         // Pretend the scheduler is busy so the first operation doesn't start right away. We want
145         // to pretend like there are two operations in the queue before kicking things off
146         mScheduler.mCurrentOperation = new BiometricSchedulerOperation(
147                 mock(BaseClientMonitor.class), mock(ClientMonitorCallback.class));
148 
149         mScheduler.scheduleClientMonitor(client1, callback1);
150         assertEquals(1, mScheduler.mPendingOperations.size());
151         // client1 is pending. Allow the scheduler to start once second client is added.
152         mScheduler.mCurrentOperation = null;
153         mScheduler.scheduleClientMonitor(client2, callback2);
154         waitForIdle();
155 
156         assertTrue(client1.mUnableToStart);
157         verify(callback1).onClientFinished(eq(client1), eq(false) /* success */);
158         verify(callback1, never()).onClientStarted(any());
159 
160         assertTrue(client2.mUnableToStart);
161         verify(callback2).onClientFinished(eq(client2), eq(false) /* success */);
162         verify(callback2, never()).onClientStarted(any());
163 
164         assertTrue(mScheduler.mPendingOperations.isEmpty());
165     }
166 
167     @Test
testRemovesOnlyBiometricPromptOperation_whenNullHal()168     public void testRemovesOnlyBiometricPromptOperation_whenNullHal() throws Exception {
169         // Second non-BiometricPrompt client has a valid daemon
170         final Object daemon2 = mock(Object.class);
171 
172         final ClientMonitorCallbackConverter listener1 = mock(ClientMonitorCallbackConverter.class);
173 
174         final TestAuthenticationClient client1 = new TestAuthenticationClient(mContext, () -> null,
175                 mToken, listener1, mBiometricContext);
176         final TestHalClientMonitor client2 = new TestHalClientMonitor(mContext, mToken,
177                 () -> daemon2);
178 
179         final ClientMonitorCallback callback1 = mock(ClientMonitorCallback.class);
180         final ClientMonitorCallback callback2 = mock(ClientMonitorCallback.class);
181 
182         // Pretend the scheduler is busy so the first operation doesn't start right away. We want
183         // to pretend like there are two operations in the queue before kicking things off
184         mScheduler.mCurrentOperation = new BiometricSchedulerOperation(
185                 mock(BaseClientMonitor.class), mock(ClientMonitorCallback.class));
186 
187         mScheduler.scheduleClientMonitor(client1, callback1);
188         assertEquals(1, mScheduler.mPendingOperations.size());
189         // client1 is pending. Allow the scheduler to start once second client is added.
190         mScheduler.mCurrentOperation = null;
191         mScheduler.scheduleClientMonitor(client2, callback2);
192         waitForIdle();
193 
194         // Simulate that the BiometricPrompt client's sensor is ready
195         mScheduler.startPreparedClient(client1.getCookie());
196 
197         // Client 1 cleans up properly
198         verify(listener1).onError(eq(TEST_SENSOR_ID), anyInt(),
199                 eq(BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE), eq(0));
200         verify(callback1).onClientFinished(eq(client1), eq(false) /* success */);
201         verify(callback1, never()).onClientStarted(any());
202 
203         // Client 2 was able to start
204         assertFalse(client2.mUnableToStart);
205         assertTrue(client2.mStarted);
206         verify(callback2).onClientStarted(eq(client2));
207     }
208 
209     @Test
testCancelNotInvoked_whenOperationWaitingForCookie()210     public void testCancelNotInvoked_whenOperationWaitingForCookie() {
211         final Supplier<Object> lazyDaemon1 = () -> mock(Object.class);
212         final TestAuthenticationClient client1 = new TestAuthenticationClient(mContext, lazyDaemon1,
213                 mToken, mock(ClientMonitorCallbackConverter.class), mBiometricContext);
214         final ClientMonitorCallback callback1 = mock(ClientMonitorCallback.class);
215 
216         // Schedule a BiometricPrompt authentication request
217         mScheduler.scheduleClientMonitor(client1, callback1);
218 
219         assertNotEquals(0,
220                 mScheduler.mCurrentOperation.isReadyToStart(mock(ClientMonitorCallback.class)));
221         assertEquals(client1, mScheduler.mCurrentOperation.getClientMonitor());
222         assertEquals(0, mScheduler.mPendingOperations.size());
223 
224         // Request it to be canceled. The operation can be canceled immediately, and the scheduler
225         // should go back to idle, since in this case the framework has not even requested the HAL
226         // to authenticate yet.
227         mScheduler.cancelAuthenticationOrDetection(mToken, 1 /* requestId */);
228         waitForIdle();
229         assertTrue(client1.isAlreadyDone());
230         assertTrue(client1.mDestroyed);
231         assertFalse(client1.mStartedHal);
232         assertNull(mScheduler.mCurrentOperation);
233     }
234 
235     @Test
testProtoDump_singleCurrentOperation()236     public void testProtoDump_singleCurrentOperation() throws Exception {
237         // Nothing so far
238         BiometricSchedulerProto bsp = getDump(true /* clearSchedulerBuffer */);
239         assertEquals(BiometricsProto.CM_NONE, bsp.currentOperation);
240         assertEquals(0, bsp.totalOperations);
241         // TODO:(b/178828362) See bug and/or commit message :/
242         // assertEquals(0, bsp.recentOperations.length);
243 
244         // Pretend the scheduler is busy enrolling, and check the proto dump again.
245         final TestHalClientMonitor client = new TestHalClientMonitor(mContext, mToken,
246                 () -> mock(Object.class), 0, BiometricsProto.CM_ENROLL);
247         mScheduler.scheduleClientMonitor(client);
248         waitForIdle();
249         bsp = getDump(true /* clearSchedulerBuffer */);
250         assertEquals(BiometricsProto.CM_ENROLL, bsp.currentOperation);
251         // No operations have completed yet
252         assertEquals(0, bsp.totalOperations);
253 
254         // TODO:(b/178828362) See bug and/or commit message :/
255         assertEquals(1, bsp.recentOperations.length);
256         assertEquals(BiometricsProto.CM_NONE, bsp.recentOperations[0]);
257 
258         // Finish this operation, so the next scheduled one can start
259         client.getCallback().onClientFinished(client, true);
260     }
261 
262     @Test
testProtoDump_fifo()263     public void testProtoDump_fifo() throws Exception {
264         // Add the first operation
265         final TestHalClientMonitor client = new TestHalClientMonitor(mContext, mToken,
266                 () -> mock(Object.class), 0, BiometricsProto.CM_ENROLL);
267         mScheduler.scheduleClientMonitor(client);
268         waitForIdle();
269         BiometricSchedulerProto bsp = getDump(false /* clearSchedulerBuffer */);
270         assertEquals(BiometricsProto.CM_ENROLL, bsp.currentOperation);
271         // No operations have completed yet
272         assertEquals(0, bsp.totalOperations);
273         // TODO:(b/178828362) See bug and/or commit message :/
274         // assertEquals(0, bsp.recentOperations.length);
275         // Finish this operation, so the next scheduled one can start
276         client.getCallback().onClientFinished(client, true);
277 
278         // Add another operation
279         final TestHalClientMonitor client2 = new TestHalClientMonitor(mContext, mToken,
280                 () -> mock(Object.class), 0, BiometricsProto.CM_REMOVE);
281         mScheduler.scheduleClientMonitor(client2);
282         waitForIdle();
283         bsp = getDump(false /* clearSchedulerBuffer */);
284         assertEquals(BiometricsProto.CM_REMOVE, bsp.currentOperation);
285         assertEquals(1, bsp.totalOperations); // Enroll finished
286         assertEquals(1, bsp.recentOperations.length);
287         assertEquals(BiometricsProto.CM_ENROLL, bsp.recentOperations[0]);
288         client2.getCallback().onClientFinished(client2, true);
289 
290         // And another operation
291         final TestHalClientMonitor client3 = new TestHalClientMonitor(mContext, mToken,
292                 () -> mock(Object.class), 0, BiometricsProto.CM_AUTHENTICATE);
293         mScheduler.scheduleClientMonitor(client3);
294         waitForIdle();
295         bsp = getDump(false /* clearSchedulerBuffer */);
296         assertEquals(BiometricsProto.CM_AUTHENTICATE, bsp.currentOperation);
297         assertEquals(2, bsp.totalOperations);
298         assertEquals(2, bsp.recentOperations.length);
299         assertEquals(BiometricsProto.CM_ENROLL, bsp.recentOperations[0]);
300         assertEquals(BiometricsProto.CM_REMOVE, bsp.recentOperations[1]);
301 
302         // Finish the last operation, and check that the first operation is removed from the FIFO.
303         // The test initializes the scheduler with "LOG_NUM_RECENT_OPERATIONS = 2" :)
304         client3.getCallback().onClientFinished(client3, true);
305         waitForIdle();
306         bsp = getDump(true /* clearSchedulerBuffer */);
307         assertEquals(3, bsp.totalOperations);
308         assertEquals(2, bsp.recentOperations.length);
309         assertEquals(BiometricsProto.CM_REMOVE, bsp.recentOperations[0]);
310         assertEquals(BiometricsProto.CM_AUTHENTICATE, bsp.recentOperations[1]);
311         // Nothing is currently running anymore
312         assertEquals(BiometricsProto.CM_NONE, bsp.currentOperation);
313 
314         // RecentOperations queue is cleared (by the previous dump)
315         bsp = getDump(true /* clearSchedulerBuffer */);
316 
317         // TODO:(b/178828362) See bug and/or commit message :/
318         assertEquals(1, bsp.recentOperations.length);
319         assertEquals(BiometricsProto.CM_NONE, bsp.recentOperations[0]);
320     }
321 
322     @Test
testCancelPendingAuth()323     public void testCancelPendingAuth() throws RemoteException {
324         final Supplier<Object> lazyDaemon = () -> mock(Object.class);
325         final TestHalClientMonitor client1 = new TestHalClientMonitor(mContext, mToken, lazyDaemon);
326         final ClientMonitorCallbackConverter callback = mock(ClientMonitorCallbackConverter.class);
327         final TestAuthenticationClient client2 = new TestAuthenticationClient(mContext, lazyDaemon,
328                 mToken, callback, mBiometricContext);
329 
330         // Add a non-cancellable client, then add the auth client
331         mScheduler.scheduleClientMonitor(client1);
332         mScheduler.scheduleClientMonitor(client2);
333         waitForIdle();
334 
335         assertEquals(mScheduler.getCurrentClient(), client1);
336         assertFalse(mScheduler.mPendingOperations.getFirst().isStarted());
337 
338         // Request cancel before the authentication client has started
339         mScheduler.cancelAuthenticationOrDetection(mToken, 1 /* requestId */);
340         waitForIdle();
341         assertTrue(mScheduler.mPendingOperations.getFirst().isMarkedCanceling());
342 
343         // Finish the blocking client. The authentication client should send ERROR_CANCELED
344         client1.getCallback().onClientFinished(client1, true /* success */);
345         waitForIdle();
346         verify(callback).onError(anyInt(), anyInt(),
347                 eq(BIOMETRIC_ERROR_CANCELED),
348                 eq(0) /* vendorCode */);
349         assertNull(mScheduler.getCurrentClient());
350         assertTrue(client1.isAlreadyDone());
351         assertTrue(client1.mDestroyed);
352         assertTrue(client2.isAlreadyDone());
353         assertTrue(client2.mDestroyed);
354     }
355 
356     @Test
testCancels_whenAuthRequestIdNotSet()357     public void testCancels_whenAuthRequestIdNotSet() {
358         testCancelsAuthDetectWhenRequestId(null /* requestId */, 2, true /* started */);
359     }
360 
361     @Test
testCancels_whenAuthRequestIdNotSet_notStarted()362     public void testCancels_whenAuthRequestIdNotSet_notStarted() {
363         testCancelsAuthDetectWhenRequestId(null /* requestId */, 2, false /* started */);
364     }
365 
366     @Test
testCancels_whenAuthRequestIdMatches()367     public void testCancels_whenAuthRequestIdMatches() {
368         testCancelsAuthDetectWhenRequestId(200L, 200, true /* started */);
369     }
370 
371     @Test
testCancels_whenAuthRequestIdMatches_noStarted()372     public void testCancels_whenAuthRequestIdMatches_noStarted() {
373         testCancelsAuthDetectWhenRequestId(200L, 200, false /* started */);
374     }
375 
376     @Test
testDoesNotCancel_whenAuthRequestIdMismatched()377     public void testDoesNotCancel_whenAuthRequestIdMismatched() {
378         testCancelsAuthDetectWhenRequestId(10L, 20, true /* started */);
379     }
380 
381     @Test
testDoesNotCancel_whenAuthRequestIdMismatched_notStarted()382     public void testDoesNotCancel_whenAuthRequestIdMismatched_notStarted() {
383         testCancelsAuthDetectWhenRequestId(10L, 20, false /* started */);
384     }
385 
testCancelsAuthDetectWhenRequestId(@ullable Long requestId, long cancelRequestId, boolean started)386     private void testCancelsAuthDetectWhenRequestId(@Nullable Long requestId, long cancelRequestId,
387             boolean started) {
388         final Supplier<Object> lazyDaemon = () -> mock(Object.class);
389         final ClientMonitorCallbackConverter callback = mock(ClientMonitorCallbackConverter.class);
390         testCancelsWhenRequestId(requestId, cancelRequestId, started,
391                 new TestAuthenticationClient(mContext, lazyDaemon, mToken, callback,
392                         mBiometricContext));
393     }
394 
395     @Test
testCancels_whenEnrollRequestIdNotSet()396     public void testCancels_whenEnrollRequestIdNotSet() {
397         testCancelsEnrollWhenRequestId(null /* requestId */, 2, false /* started */);
398     }
399 
400     @Test
testCancels_whenEnrollRequestIdMatches()401     public void testCancels_whenEnrollRequestIdMatches() {
402         testCancelsEnrollWhenRequestId(200L, 200, false /* started */);
403     }
404 
405     @Test
testDoesNotCancel_whenEnrollRequestIdMismatched()406     public void testDoesNotCancel_whenEnrollRequestIdMismatched() {
407         testCancelsEnrollWhenRequestId(10L, 20, false /* started */);
408     }
409 
410     @Test
testCancelAuthenticationClientWithoutStarting()411     public void testCancelAuthenticationClientWithoutStarting() {
412         final Supplier<Object> lazyDaemon = () -> mock(Object.class);
413         final TestHalClientMonitor client1 = new TestHalClientMonitor(mContext, mToken, lazyDaemon);
414         final ClientMonitorCallbackConverter callback = mock(ClientMonitorCallbackConverter.class);
415         final TestAuthenticationClient client2 = new TestAuthenticationClient(mContext, lazyDaemon,
416                 mToken, callback, mBiometricContext);
417 
418         //Schedule authentication client to the pending queue
419         mScheduler.scheduleClientMonitor(client1);
420         mScheduler.scheduleClientMonitor(client2);
421         waitForIdle();
422 
423         assertThat(mScheduler.getCurrentClient()).isEqualTo(client1);
424 
425         client2.cancel();
426         waitForIdle();
427 
428         assertThat(client2.isAlreadyCancelled()).isTrue();
429 
430         client1.getCallback().onClientFinished(client1, false);
431         waitForIdle();
432 
433         assertThat(mScheduler.getCurrentClient()).isNull();
434     }
435 
436     @Test
testCancelAuthenticationClientWithoutStarting_whenAppCrashes()437     public void testCancelAuthenticationClientWithoutStarting_whenAppCrashes() {
438         final Supplier<Object> lazyDaemon = () -> mock(Object.class);
439         final TestHalClientMonitor client1 = new TestHalClientMonitor(mContext, mToken, lazyDaemon);
440         final ClientMonitorCallbackConverter callback = mock(ClientMonitorCallbackConverter.class);
441         final TestAuthenticationClient client2 = new TestAuthenticationClient(mContext, lazyDaemon,
442                 mToken, callback, mBiometricContext);
443 
444         //Schedule authentication client to the pending queue
445         mScheduler.scheduleClientMonitor(client1);
446         mScheduler.scheduleClientMonitor(client2);
447         waitForIdle();
448 
449         assertThat(mScheduler.getCurrentClient()).isEqualTo(client1);
450 
451         //App crashes
452         client2.binderDied();
453         waitForIdle();
454 
455         assertThat(client2.isAlreadyCancelled()).isTrue();
456 
457         client1.getCallback().onClientFinished(client1, false);
458         waitForIdle();
459 
460         assertThat(mScheduler.getCurrentClient()).isNull();
461     }
462 
testCancelsEnrollWhenRequestId(@ullable Long requestId, long cancelRequestId, boolean started)463     private void testCancelsEnrollWhenRequestId(@Nullable Long requestId, long cancelRequestId,
464             boolean started) {
465         final Supplier<Object> lazyDaemon = () -> mock(Object.class);
466         final ClientMonitorCallbackConverter callback = mock(ClientMonitorCallbackConverter.class);
467         testCancelsWhenRequestId(requestId, cancelRequestId, started,
468                 new TestEnrollClient(mContext, lazyDaemon, mToken, callback));
469     }
470 
testCancelsWhenRequestId(@ullable Long requestId, long cancelRequestId, boolean started, HalClientMonitor<?> client)471     private void testCancelsWhenRequestId(@Nullable Long requestId, long cancelRequestId,
472             boolean started, HalClientMonitor<?> client) {
473         final boolean matches = requestId == null || requestId == cancelRequestId;
474         if (requestId != null) {
475             client.setRequestId(requestId);
476         }
477 
478         final boolean isAuth = client instanceof TestAuthenticationClient;
479         final boolean isEnroll = client instanceof TestEnrollClient;
480 
481         mScheduler.scheduleClientMonitor(client);
482         if (started) {
483             mScheduler.startPreparedClient(client.getCookie());
484         }
485         waitForIdle();
486         if (isAuth) {
487             mScheduler.cancelAuthenticationOrDetection(mToken, cancelRequestId);
488         } else if (isEnroll) {
489             mScheduler.cancelEnrollment(mToken, cancelRequestId);
490         } else {
491             fail("unexpected operation type");
492         }
493         waitForIdle();
494 
495         if (isAuth) {
496             // auth clients that were waiting for cookie when canceled should never invoke the hal
497             final TestAuthenticationClient authClient = (TestAuthenticationClient) client;
498             assertEquals(matches && started ? 1 : 0, authClient.mNumCancels);
499             assertEquals(started, authClient.mStartedHal);
500         } else if (isEnroll) {
501             final TestEnrollClient enrollClient = (TestEnrollClient) client;
502             assertEquals(matches ? 1 : 0, enrollClient.mNumCancels);
503             assertTrue(enrollClient.mStartedHal);
504         }
505 
506         if (matches) {
507             if (started || isEnroll) { // prep'd auth clients and enroll clients
508                 assertTrue(mScheduler.mCurrentOperation.isCanceling());
509             }
510         } else {
511             if (started || isEnroll) { // prep'd auth clients and enroll clients
512                 assertTrue(mScheduler.mCurrentOperation.isStarted());
513             } else {
514                 assertNotEquals(0, mScheduler.mCurrentOperation.isReadyToStart(
515                         mock(ClientMonitorCallback.class)));
516             }
517         }
518     }
519 
520     @Test
testCancelsPending_whenAuthRequestIdsSet()521     public void testCancelsPending_whenAuthRequestIdsSet() {
522         final long requestId1 = 10;
523         final long requestId2 = 20;
524         final Supplier<Object> lazyDaemon = () -> mock(Object.class);
525         final ClientMonitorCallbackConverter callback = mock(ClientMonitorCallbackConverter.class);
526         final TestAuthenticationClient client1 = new TestAuthenticationClient(mContext, lazyDaemon,
527                 mToken, callback, mBiometricContext);
528         client1.setRequestId(requestId1);
529         final TestAuthenticationClient client2 = new TestAuthenticationClient(mContext, lazyDaemon,
530                 mToken, callback, mBiometricContext);
531         client2.setRequestId(requestId2);
532 
533         mScheduler.scheduleClientMonitor(client1);
534         mScheduler.scheduleClientMonitor(client2);
535         mScheduler.startPreparedClient(client1.getCookie());
536         waitForIdle();
537         mScheduler.cancelAuthenticationOrDetection(mToken, 9999);
538         waitForIdle();
539 
540         assertTrue(mScheduler.mCurrentOperation.isStarted());
541         assertFalse(mScheduler.mPendingOperations.getFirst().isStarted());
542 
543         mScheduler.cancelAuthenticationOrDetection(mToken, requestId2);
544         waitForIdle();
545 
546         assertTrue(mScheduler.mCurrentOperation.isStarted());
547         assertTrue(mScheduler.mPendingOperations.getFirst().isMarkedCanceling());
548     }
549 
550     @Test
testInterruptPrecedingClients_whenExpected()551     public void testInterruptPrecedingClients_whenExpected() {
552         final BaseClientMonitor interruptableMonitor = mock(BaseClientMonitor.class);
553         when(interruptableMonitor.isInterruptable()).thenReturn(true);
554 
555         final BaseClientMonitor interrupter = mock(BaseClientMonitor.class);
556         when(interrupter.interruptsPrecedingClients()).thenReturn(true);
557 
558         mScheduler.scheduleClientMonitor(interruptableMonitor);
559         mScheduler.scheduleClientMonitor(interrupter);
560         waitForIdle();
561 
562         verify(interruptableMonitor).cancel();
563         mScheduler.getInternalCallback().onClientFinished(interruptableMonitor, true /* success */);
564     }
565 
566     @Test
testDoesNotInterruptPrecedingClients_whenNotExpected()567     public void testDoesNotInterruptPrecedingClients_whenNotExpected() {
568         final BaseClientMonitor interruptableMonitor = mock(BaseClientMonitor.class);
569         when(interruptableMonitor.isInterruptable()).thenReturn(true);
570 
571         final BaseClientMonitor interrupter = mock(BaseClientMonitor.class);
572         when(interrupter.interruptsPrecedingClients()).thenReturn(false);
573 
574         mScheduler.scheduleClientMonitor(interruptableMonitor);
575         mScheduler.scheduleClientMonitor(interrupter);
576         waitForIdle();
577 
578         verify(interruptableMonitor, never()).cancel();
579     }
580 
581     @Test
testClientDestroyed_afterFinish()582     public void testClientDestroyed_afterFinish() {
583         final Supplier<Object> nonNullDaemon = () -> mock(Object.class);
584         final TestHalClientMonitor client = new TestHalClientMonitor(mContext, mToken,
585                 nonNullDaemon);
586         mScheduler.scheduleClientMonitor(client);
587         client.mCallback.onClientFinished(client, true /* success */);
588         waitForIdle();
589         assertTrue(client.mDestroyed);
590     }
591 
592     @Test
testClearBiometricQueue_clearsHungAuthOperation()593     public void testClearBiometricQueue_clearsHungAuthOperation() {
594         // Creating a hung client
595         final TestableLooper looper = TestableLooper.get(this);
596         final Supplier<Object> lazyDaemon1 = () -> mock(Object.class);
597         final TestAuthenticationClient client1 = new TestAuthenticationClient(mContext,
598                 lazyDaemon1, mToken, mock(ClientMonitorCallbackConverter.class), 0 /* cookie */,
599                 mBiometricContext);
600         final ClientMonitorCallback callback1 = mock(ClientMonitorCallback.class);
601 
602         mScheduler.scheduleClientMonitor(client1, callback1);
603         waitForIdle();
604 
605         mScheduler.startWatchdog();
606         waitForIdle();
607 
608         //Checking client is hung
609         verify(callback1).onClientStarted(client1);
610         verify(callback1, never()).onClientFinished(any(), anyBoolean());
611         assertNotNull(mScheduler.mCurrentOperation);
612         assertEquals(0, mScheduler.getCurrentPendingCount());
613 
614         looper.moveTimeForward(10000);
615         waitForIdle();
616         looper.moveTimeForward(3000);
617         waitForIdle();
618 
619         // The hung client did not honor this operation, verify onError and authenticated
620         // were never called.
621         assertFalse(client1.mOnErrorCalled);
622         assertFalse(client1.mAuthenticateCalled);
623         verify(callback1).onClientFinished(client1, false /* success */);
624         assertNull(mScheduler.mCurrentOperation);
625         assertEquals(0, mScheduler.getCurrentPendingCount());
626     }
627 
628     @Test
testAuthWorks_afterClearBiometricQueue()629     public void testAuthWorks_afterClearBiometricQueue() {
630         // Creating a hung client
631         final TestableLooper looper = TestableLooper.get(this);
632         final Supplier<Object> lazyDaemon1 = () -> mock(Object.class);
633         final TestAuthenticationClient client1 = new TestAuthenticationClient(mContext,
634                 lazyDaemon1, mToken, mock(ClientMonitorCallbackConverter.class), 0 /* cookie */,
635                 mBiometricContext);
636         final ClientMonitorCallback callback1 = mock(ClientMonitorCallback.class);
637 
638         mScheduler.scheduleClientMonitor(client1, callback1);
639 
640         assertEquals(client1, mScheduler.mCurrentOperation.getClientMonitor());
641         assertEquals(0, mScheduler.getCurrentPendingCount());
642 
643         //Checking client is hung
644         waitForIdle();
645         verify(callback1, never()).onClientFinished(any(), anyBoolean());
646 
647         //Start watchdog
648         mScheduler.startWatchdog();
649         waitForIdle();
650 
651         // The watchdog should kick off the cancellation
652         looper.moveTimeForward(10000);
653         waitForIdle();
654         // After 10 seconds the HAL has 3 seconds to respond to a cancel
655         looper.moveTimeForward(3000);
656         waitForIdle();
657 
658         // The hung client did not honor this operation, verify onError and authenticated
659         // were never called.
660         assertFalse(client1.mOnErrorCalled);
661         assertFalse(client1.mAuthenticateCalled);
662         verify(callback1).onClientFinished(client1, false /* success */);
663         assertEquals(0, mScheduler.getCurrentPendingCount());
664         assertNull(mScheduler.mCurrentOperation);
665 
666 
667         //Run additional auth client
668         final TestAuthenticationClient client2 = new TestAuthenticationClient(mContext,
669                 lazyDaemon1, mToken, mock(ClientMonitorCallbackConverter.class), 0 /* cookie */,
670                 mBiometricContext);
671         final ClientMonitorCallback callback2 = mock(ClientMonitorCallback.class);
672 
673         mScheduler.scheduleClientMonitor(client2, callback2);
674 
675         assertEquals(client2, mScheduler.mCurrentOperation.getClientMonitor());
676         assertEquals(0, mScheduler.getCurrentPendingCount());
677 
678         //Start watchdog
679         mScheduler.startWatchdog();
680         waitForIdle();
681         mScheduler.scheduleClientMonitor(mock(BaseClientMonitor.class),
682                 mock(ClientMonitorCallback.class));
683         waitForIdle();
684 
685         //Ensure auth client passes
686         verify(callback2).onClientStarted(client2);
687         client2.getCallback().onClientFinished(client2, true);
688         waitForIdle();
689 
690         looper.moveTimeForward(10000);
691         waitForIdle();
692         // After 10 seconds the HAL has 3 seconds to respond to a cancel
693         looper.moveTimeForward(3000);
694         waitForIdle();
695 
696         //Asserting auth client passes
697         assertTrue(client2.isAlreadyDone());
698         assertNotNull(mScheduler.mCurrentOperation);
699     }
700 
701     @Test
testClearBiometricQueue_doesNotClearOperationsWhenQueueNotStuck()702     public void testClearBiometricQueue_doesNotClearOperationsWhenQueueNotStuck() {
703         //Creating clients
704         final TestableLooper looper = TestableLooper.get(this);
705         final Supplier<Object> lazyDaemon1 = () -> mock(Object.class);
706         final TestAuthenticationClient client1 = new TestAuthenticationClient(mContext,
707                 lazyDaemon1, mToken, mock(ClientMonitorCallbackConverter.class), 0 /* cookie */,
708                 mBiometricContext);
709         final ClientMonitorCallback callback1 = mock(ClientMonitorCallback.class);
710 
711         mScheduler.scheduleClientMonitor(client1, callback1);
712         //Start watchdog
713         mScheduler.startWatchdog();
714         waitForIdle();
715         mScheduler.scheduleClientMonitor(mock(BaseClientMonitor.class),
716                 mock(ClientMonitorCallback.class));
717         mScheduler.scheduleClientMonitor(mock(BaseClientMonitor.class),
718                 mock(ClientMonitorCallback.class));
719         waitForIdle();
720 
721         assertEquals(client1, mScheduler.mCurrentOperation.getClientMonitor());
722         assertEquals(2, mScheduler.getCurrentPendingCount());
723         verify(callback1, never()).onClientFinished(any(), anyBoolean());
724         verify(callback1).onClientStarted(client1);
725 
726         //Client finishes successfully
727         client1.getCallback().onClientFinished(client1, true);
728         waitForIdle();
729 
730         // The watchdog should kick off the cancellation
731         looper.moveTimeForward(10000);
732         waitForIdle();
733         // After 10 seconds the HAL has 3 seconds to respond to a cancel
734         looper.moveTimeForward(3000);
735         waitForIdle();
736 
737         //Watchdog does not clear pending operations
738         assertEquals(1, mScheduler.getCurrentPendingCount());
739         assertNotNull(mScheduler.mCurrentOperation);
740 
741     }
742 
743     @Test
testTwoInternalCleanupOps_withFirstFavorHalEnrollment()744     public void testTwoInternalCleanupOps_withFirstFavorHalEnrollment() throws Exception {
745         final String owner = "test.owner";
746         final int userId = 1;
747         final Supplier<Object> daemon = () -> mock(AidlSession.class);
748         final FingerprintUtils utils = mock(FingerprintUtils.class);
749         final Map<Integer, Long> authenticatorIds = new HashMap<>();
750         final ClientMonitorCallback callback0 = mock(ClientMonitorCallback.class);
751         final ClientMonitorCallback callback1 = mock(ClientMonitorCallback.class);
752         final ClientMonitorCallback callback2 = mock(ClientMonitorCallback.class);
753 
754         final TestInternalCleanupClient client1 = new
755                 TestInternalCleanupClient(mContext, daemon, userId,
756                 owner, TEST_SENSOR_ID, mock(BiometricLogger.class),
757                 mBiometricContext, utils, authenticatorIds);
758         final TestInternalCleanupClient client2 = new
759                 TestInternalCleanupClient(mContext, daemon, userId,
760                 owner, TEST_SENSOR_ID, mock(BiometricLogger.class),
761                 mBiometricContext, utils, authenticatorIds);
762 
763         //add initial start client to scheduler, so later clients will be on pending operation queue
764         final TestHalClientMonitor startClient = new TestHalClientMonitor(mContext, mToken,
765                 daemon);
766         mScheduler.scheduleClientMonitor(startClient, callback0);
767 
768         //add first cleanup client which favors enrollments from HAL
769         client1.setFavorHalEnrollments();
770         mScheduler.scheduleClientMonitor(client1, callback1);
771         assertEquals(1, mScheduler.mPendingOperations.size());
772 
773         when(utils.getBiometricsForUser(mContext, userId)).thenAnswer(i ->
774                 new ArrayList<>(client1.getFingerprints()));
775 
776         //add second cleanup client
777         mScheduler.scheduleClientMonitor(client2, callback2);
778 
779         //finish the start client, so other pending clients are processed
780         startClient.getCallback().onClientFinished(startClient, true);
781 
782         waitForIdle();
783 
784         assertTrue(client1.isAlreadyDone());
785         assertTrue(client2.isAlreadyDone());
786         assertNull(mScheduler.mCurrentOperation);
787         verify(utils, never()).removeBiometricForUser(mContext, userId,
788                 TEST_FINGERPRINT.getBiometricId());
789         assertEquals(1,  client1.getFingerprints().size());
790     }
791 
792 
getDump(boolean clearSchedulerBuffer)793     private BiometricSchedulerProto getDump(boolean clearSchedulerBuffer) throws Exception {
794         return BiometricSchedulerProto.parseFrom(mScheduler.dumpProtoState(clearSchedulerBuffer));
795     }
796 
waitForIdle()797     private void waitForIdle() {
798         TestableLooper.get(this).processAllMessages();
799     }
800 
801     private static class TestAuthenticateOptions implements AuthenticateOptions {
802         @Override
getUserId()803         public int getUserId() {
804             return 0;
805         }
806 
807         @Override
getSensorId()808         public int getSensorId() {
809             return TEST_SENSOR_ID;
810         }
811 
812         @Override
getDisplayState()813         public int getDisplayState() {
814             return DISPLAY_STATE_UNKNOWN;
815         }
816 
817         @NonNull
818         @Override
getOpPackageName()819         public String getOpPackageName() {
820             return "some.test.name";
821         }
822 
823         @Nullable
824         @Override
getAttributionTag()825         public String getAttributionTag() {
826             return null;
827         }
828     }
829 
830     private static class TestAuthenticationClient
831             extends AuthenticationClient<Object, TestAuthenticateOptions> {
832         boolean mStartedHal = false;
833         boolean mStoppedHal = false;
834         boolean mDestroyed = false;
835         int mNumCancels = 0;
836         boolean mAuthenticateCalled = false;
837         boolean mOnErrorCalled = false;
838 
TestAuthenticationClient(@onNull Context context, @NonNull Supplier<Object> lazyDaemon, @NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener, BiometricContext biometricContext)839         TestAuthenticationClient(@NonNull Context context,
840                 @NonNull Supplier<Object> lazyDaemon, @NonNull IBinder token,
841                 @NonNull ClientMonitorCallbackConverter listener,
842                 BiometricContext biometricContext) {
843             this(context, lazyDaemon, token, listener, 1 /* cookie */, biometricContext);
844         }
845 
TestAuthenticationClient(@onNull Context context, @NonNull Supplier<Object> lazyDaemon, @NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener, int cookie, @NonNull BiometricContext biometricContext)846         TestAuthenticationClient(@NonNull Context context,
847                 @NonNull Supplier<Object> lazyDaemon, @NonNull IBinder token,
848                 @NonNull ClientMonitorCallbackConverter listener, int cookie,
849                 @NonNull BiometricContext biometricContext) {
850             super(context, lazyDaemon, token, listener, 0 /* operationId */,
851                     false /* restricted */, new TestAuthenticateOptions(), cookie,
852                     false /* requireConfirmation */,
853                     mock(BiometricLogger.class), biometricContext,
854                     true /* isStrongBiometric */, null /* taskStackListener */,
855                     null /* lockoutTracker */, false /* isKeyguard */,
856                     true /* shouldVibrate */,
857                     0 /* sensorStrength */);
858         }
859 
860         @Override
stopHalOperation()861         protected void stopHalOperation() {
862             mStoppedHal = true;
863         }
864 
865         @Override
startHalOperation()866         protected void startHalOperation() {
867             mStartedHal = true;
868         }
869 
870         @Override
handleLifecycleAfterAuth(boolean authenticated)871         protected void handleLifecycleAfterAuth(boolean authenticated) {
872         }
873 
874         @Override
onAuthenticated(BiometricAuthenticator.Identifier identifier, boolean authenticated, ArrayList<Byte> hardwareAuthToken)875         public void onAuthenticated(BiometricAuthenticator.Identifier identifier,
876                 boolean authenticated, ArrayList<Byte> hardwareAuthToken) {
877             mAuthenticateCalled = true;
878         }
879 
880         @Override
onErrorInternal(int errorCode, int vendorCode, boolean finish)881         protected void onErrorInternal(int errorCode, int vendorCode, boolean finish) {
882             mOnErrorCalled = true;
883         }
884 
885         @Override
wasUserDetected()886         public boolean wasUserDetected() {
887             return false;
888         }
889 
890         @Override
destroy()891         public void destroy() {
892             mDestroyed = true;
893             super.destroy();
894         }
895 
896         @Override
cancel()897         public void cancel() {
898             mNumCancels++;
899             super.cancel();
900         }
901     }
902 
903     private static class TestEnrollClient extends EnrollClient<Object> {
904         boolean mStartedHal = false;
905         boolean mStoppedHal = false;
906         int mNumCancels = 0;
907 
TestEnrollClient(@onNull Context context, @NonNull Supplier<Object> lazyDaemon, @NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener)908         TestEnrollClient(@NonNull Context context, @NonNull Supplier<Object> lazyDaemon,
909                 @NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener) {
910             super(context, lazyDaemon, token, listener, 0 /* userId */, new byte[69],
911                     "test" /* owner */, mock(BiometricUtils.class), 5 /* timeoutSec */,
912                     TEST_SENSOR_ID, true /* shouldVibrate */, mock(BiometricLogger.class),
913                     mock(BiometricContext.class));
914         }
915 
916         @Override
stopHalOperation()917         protected void stopHalOperation() {
918             mStoppedHal = true;
919         }
920 
921         @Override
startHalOperation()922         protected void startHalOperation() {
923             mStartedHal = true;
924         }
925 
926         @Override
hasReachedEnrollmentLimit()927         protected boolean hasReachedEnrollmentLimit() {
928             return false;
929         }
930 
931         @Override
cancel()932         public void cancel() {
933             mNumCancels++;
934             super.cancel();
935         }
936     }
937 
938     private static class TestHalClientMonitor extends HalClientMonitor<Object> {
939         private final int mProtoEnum;
940         private boolean mUnableToStart;
941         private boolean mStarted;
942         private boolean mDestroyed;
943 
TestHalClientMonitor(@onNull Context context, @NonNull IBinder token, @NonNull Supplier<Object> lazyDaemon)944         TestHalClientMonitor(@NonNull Context context, @NonNull IBinder token,
945                 @NonNull Supplier<Object> lazyDaemon) {
946             this(context, token, lazyDaemon, 0 /* cookie */, BiometricsProto.CM_UPDATE_ACTIVE_USER);
947         }
948 
TestHalClientMonitor(@onNull Context context, @NonNull IBinder token, @NonNull Supplier<Object> lazyDaemon, int cookie, int protoEnum)949         TestHalClientMonitor(@NonNull Context context, @NonNull IBinder token,
950                 @NonNull Supplier<Object> lazyDaemon, int cookie, int protoEnum) {
951             super(context, lazyDaemon, token /* token */, null /* listener */, 0 /* userId */, TAG,
952                     cookie, TEST_SENSOR_ID, mock(BiometricLogger.class),
953                     mock(BiometricContext.class));
954             mProtoEnum = protoEnum;
955         }
956 
957         @Override
unableToStart()958         public void unableToStart() {
959             assertFalse(mUnableToStart);
960             mUnableToStart = true;
961         }
962 
963         @Override
getProtoEnum()964         public int getProtoEnum() {
965             return mProtoEnum;
966         }
967 
968         @Override
start(@onNull ClientMonitorCallback callback)969         public void start(@NonNull ClientMonitorCallback callback) {
970             super.start(callback);
971             assertFalse(mStarted);
972             mStarted = true;
973         }
974 
975         @Override
startHalOperation()976         protected void startHalOperation() {
977             mStarted = true;
978         }
979 
980         @Override
destroy()981         public void destroy() {
982             super.destroy();
983             mDestroyed = true;
984         }
985     }
986 
987     private static class TestInternalEnumerateClient extends InternalEnumerateClient<Object> {
988         private static final String TAG = "TestInternalEnumerateClient";
989 
990 
TestInternalEnumerateClient(@onNull Context context, @NonNull Supplier<Object> lazyDaemon, @NonNull IBinder token, int userId, @NonNull String owner, @NonNull List<Fingerprint> enrolledList, @NonNull BiometricUtils<Fingerprint> utils, int sensorId, @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext)991         protected TestInternalEnumerateClient(@NonNull Context context,
992                 @NonNull Supplier<Object> lazyDaemon, @NonNull IBinder token, int userId,
993                 @NonNull String owner, @NonNull List<Fingerprint> enrolledList,
994                 @NonNull BiometricUtils<Fingerprint> utils, int sensorId,
995                 @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext) {
996             super(context, lazyDaemon, token, userId, owner, enrolledList, utils, sensorId,
997                     logger, biometricContext);
998         }
999 
1000         @Override
startHalOperation()1001         protected void startHalOperation() {
1002             Slog.d(TAG, "TestInternalEnumerateClient#startHalOperation");
1003             onEnumerationResult(TEST_FINGERPRINT, 0 /* remaining */);
1004         }
1005     }
1006 
1007     private static class TestRemovalClient extends RemovalClient<Fingerprint, Object> {
1008         private static final String TAG = "TestRemovalClient";
1009 
TestRemovalClient(@onNull Context context, @NonNull Supplier<Object> lazyDaemon, @NonNull IBinder token, @Nullable ClientMonitorCallbackConverter listener, int[] biometricIds, int userId, @NonNull String owner, @NonNull BiometricUtils<Fingerprint> utils, int sensorId, @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext, @NonNull Map<Integer, Long> authenticatorIds)1010         TestRemovalClient(@NonNull Context context,
1011                 @NonNull Supplier<Object> lazyDaemon, @NonNull IBinder token,
1012                 @Nullable ClientMonitorCallbackConverter listener, int[] biometricIds, int userId,
1013                 @NonNull String owner, @NonNull BiometricUtils<Fingerprint> utils, int sensorId,
1014                 @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext,
1015                 @NonNull Map<Integer, Long> authenticatorIds) {
1016             super(context, lazyDaemon, token, listener, userId, owner, utils, sensorId,
1017                     logger, biometricContext, authenticatorIds);
1018         }
1019 
1020         @Override
startHalOperation()1021         protected void startHalOperation() {
1022             Slog.d(TAG, "Removing template from hw");
1023             onRemoved(TEST_FINGERPRINT, 0);
1024         }
1025     }
1026 
1027     private static class TestInternalCleanupClient extends
1028             InternalCleanupClient<Fingerprint, Object> {
1029         private List<Fingerprint> mFingerprints = new ArrayList<>();
1030 
TestInternalCleanupClient(@onNull Context context, @NonNull Supplier<Object> lazyDaemon, int userId, @NonNull String owner, int sensorId, @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext, @NonNull FingerprintUtils utils, @NonNull Map<Integer, Long> authenticatorIds)1031         TestInternalCleanupClient(@NonNull Context context,
1032                 @NonNull Supplier<Object> lazyDaemon,
1033                 int userId, @NonNull String owner, int sensorId,
1034                 @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext,
1035                 @NonNull FingerprintUtils utils, @NonNull Map<Integer, Long> authenticatorIds) {
1036             super(context, lazyDaemon, userId, owner, sensorId, logger, biometricContext,
1037                     utils, authenticatorIds);
1038         }
1039 
1040         @Override
getEnumerateClient(Context context, Supplier<Object> lazyDaemon, IBinder token, int userId, String owner, List<Fingerprint> enrolledList, BiometricUtils<Fingerprint> utils, int sensorId, @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext)1041         protected InternalEnumerateClient<Object> getEnumerateClient(Context context,
1042                 Supplier<Object> lazyDaemon, IBinder token, int userId, String owner,
1043                 List<Fingerprint> enrolledList, BiometricUtils<Fingerprint> utils, int sensorId,
1044                 @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext) {
1045             return new TestInternalEnumerateClient(context, lazyDaemon, token, userId, owner,
1046                     enrolledList, utils, sensorId,
1047                     logger.swapAction(context, BiometricsProtoEnums.ACTION_ENUMERATE),
1048                     biometricContext);
1049         }
1050 
1051         @Override
getRemovalClient(Context context, Supplier<Object> lazyDaemon, IBinder token, int biometricId, int userId, String owner, BiometricUtils<Fingerprint> utils, int sensorId, @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext, Map<Integer, Long> authenticatorIds)1052         protected RemovalClient<Fingerprint, Object> getRemovalClient(Context context,
1053                 Supplier<Object> lazyDaemon, IBinder token, int biometricId, int userId,
1054                 String owner, BiometricUtils<Fingerprint> utils, int sensorId,
1055                 @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext,
1056                 Map<Integer, Long> authenticatorIds) {
1057             return new TestRemovalClient(context, lazyDaemon, token, null,
1058                     new int[]{biometricId}, userId, owner, utils, sensorId, logger,
1059                     biometricContext, authenticatorIds);
1060         }
1061 
1062         @Override
onAddUnknownTemplate(int userId, @NonNull BiometricAuthenticator.Identifier identifier)1063         protected void onAddUnknownTemplate(int userId,
1064                 @NonNull BiometricAuthenticator.Identifier identifier) {
1065             mFingerprints.add((Fingerprint) identifier);
1066         }
1067 
getFingerprints()1068         public List<Fingerprint> getFingerprints() {
1069             return mFingerprints;
1070         }
1071     }
1072 }
1073