• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.fingerprint.hidl;
18 
19 import android.annotation.NonNull;
20 import android.annotation.Nullable;
21 import android.app.trust.TrustManager;
22 import android.content.ContentResolver;
23 import android.content.Context;
24 import android.hardware.fingerprint.FingerprintManager;
25 import android.hardware.fingerprint.FingerprintManager.AuthenticationCallback;
26 import android.hardware.fingerprint.FingerprintManager.AuthenticationResult;
27 import android.hardware.fingerprint.FingerprintSensorProperties;
28 import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
29 import android.hardware.fingerprint.IUdfpsOverlayController;
30 import android.os.Handler;
31 import android.os.IBinder;
32 import android.os.Looper;
33 import android.os.RemoteException;
34 import android.os.UserHandle;
35 import android.provider.Settings;
36 import android.util.Slog;
37 import android.util.SparseBooleanArray;
38 
39 import com.android.internal.R;
40 import com.android.server.biometrics.sensors.AuthenticationConsumer;
41 import com.android.server.biometrics.sensors.BaseClientMonitor;
42 import com.android.server.biometrics.sensors.BiometricScheduler;
43 import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
44 import com.android.server.biometrics.sensors.LockoutResetDispatcher;
45 import com.android.server.biometrics.sensors.fingerprint.GestureAvailabilityDispatcher;
46 
47 import java.util.ArrayList;
48 import java.util.List;
49 import java.util.Random;
50 
51 /**
52  * A mockable/testable provider of the {@link android.hardware.biometrics.fingerprint.V2_3} HIDL
53  * interface. This class is intended simulate UDFPS logic for devices that do not have an actual
54  * fingerprint@2.3 HAL (where UDFPS starts to become supported)
55  *
56  * UDFPS "accept" can only happen within a set amount of time after a sensor authentication. This is
57  * specified by {@link MockHalResultController#AUTH_VALIDITY_MS}. Touches after this duration will
58  * be treated as "reject".
59  *
60  * This class provides framework logic to emulate, for testing only, the UDFPS functionalies below:
61  *
62  * 1) IF either A) the caller is keyguard, and the device is not in a trusted state (authenticated
63  *    via biometric sensor or unlocked with a trust agent {@see android.app.trust.TrustManager}, OR
64  *    B) the caller is not keyguard, and regardless of trusted state, AND (following applies to both
65  *    (A) and (B) above) {@link FingerprintManager#onFingerDown(int, int, float, float)} is
66  *    received, this class will respond with {@link AuthenticationCallback#onAuthenticationFailed()}
67  *    after a tunable flat_time + variance_time.
68  *
69  *    In the case above (1), callers must not receive a successful authentication event here because
70  *    the sensor has not actually been authenticated.
71  *
72  * 2) IF A) the caller is keyguard and the device is not in a trusted state, OR B) the caller is not
73  *    keyguard and regardless of trusted state, AND (following applies to both (A) and (B)) the
74  *    sensor is touched and the fingerprint is accepted by the HAL, and then
75  *    {@link FingerprintManager#onFingerDown(int, int, float, float)} is received, this class will
76  *    respond with {@link AuthenticationCallback#onAuthenticationSucceeded(AuthenticationResult)}
77  *    after a tunable flat_time + variance_time. Note that the authentication callback from the
78  *    sensor is held until {@link FingerprintManager#onFingerDown(int, int, float, float)} is
79  *    invoked.
80  *
81  *    In the case above (2), callers can receive a successful authentication callback because the
82  *    real sensor was authenticated. Note that even though the real sensor was touched, keyguard
83  *    fingerprint authentication does not put keyguard into a trusted state because the
84  *    authentication callback is held until onFingerDown was invoked. This allows callers such as
85  *    keyguard to simulate a realistic path.
86  *
87  * 3) IF the caller is keyguard AND the device in a trusted state and then
88  *    {@link FingerprintManager#onFingerDown(int, int, float, float)} is received, this class will
89  *    respond with {@link AuthenticationCallback#onAuthenticationSucceeded(AuthenticationResult)}
90  *    after a tunable flat_time + variance time.
91  *
92  *    In the case above (3), since the device is already unlockable via trust agent, it's fine to
93  *    simulate the successful auth path. Non-keyguard clients will fall into either (1) or (2)
94  *    above.
95  *
96  *  This class currently does not simulate false rejection. Conversely, this class relies on the
97  *  real hardware sensor so does not affect false acceptance.
98  */
99 @SuppressWarnings("deprecation")
100 public class Fingerprint21UdfpsMock extends Fingerprint21 implements TrustManager.TrustListener {
101 
102     private static final String TAG = "Fingerprint21UdfpsMock";
103 
104     // Secure setting integer. If true, the system will load this class to enable udfps testing.
105     public static final String CONFIG_ENABLE_TEST_UDFPS =
106             "com.android.server.biometrics.sensors.fingerprint.test_udfps.enable";
107     // Secure setting integer. A fixed duration intended to simulate something like the duration
108     // required for image capture.
109     private static final String CONFIG_AUTH_DELAY_PT1 =
110             "com.android.server.biometrics.sensors.fingerprint.test_udfps.auth_delay_pt1";
111     // Secure setting integer. A fixed duration intended to simulate something like the duration
112     // required for template matching to complete.
113     private static final String CONFIG_AUTH_DELAY_PT2 =
114             "com.android.server.biometrics.sensors.fingerprint.test_udfps.auth_delay_pt2";
115     // Secure setting integer. A random value between [-randomness, randomness] will be added to the
116     // capture delay above for each accept/reject.
117     private static final String CONFIG_AUTH_DELAY_RANDOMNESS =
118             "com.android.server.biometrics.sensors.fingerprint.test_udfps.auth_delay_randomness";
119 
120     private static final int DEFAULT_AUTH_DELAY_PT1_MS = 300;
121     private static final int DEFAULT_AUTH_DELAY_PT2_MS = 400;
122     private static final int DEFAULT_AUTH_DELAY_RANDOMNESS_MS = 100;
123 
124     @NonNull private final TestableBiometricScheduler mScheduler;
125     @NonNull private final Handler mHandler;
126     @NonNull private final FingerprintSensorPropertiesInternal mSensorProperties;
127     @NonNull private final MockHalResultController mMockHalResultController;
128     @NonNull private final TrustManager mTrustManager;
129     @NonNull private final SparseBooleanArray mUserHasTrust;
130     @NonNull private final Random mRandom;
131     @NonNull private final FakeRejectRunnable mFakeRejectRunnable;
132     @NonNull private final FakeAcceptRunnable mFakeAcceptRunnable;
133     @NonNull private final RestartAuthRunnable mRestartAuthRunnable;
134 
135     private static class TestableBiometricScheduler extends BiometricScheduler {
136         @NonNull private final TestableInternalCallback mInternalCallback;
137         @NonNull private Fingerprint21UdfpsMock mFingerprint21;
138 
TestableBiometricScheduler(@onNull String tag, @Nullable GestureAvailabilityDispatcher gestureAvailabilityDispatcher)139         TestableBiometricScheduler(@NonNull String tag,
140                 @Nullable GestureAvailabilityDispatcher gestureAvailabilityDispatcher) {
141             super(tag, BiometricScheduler.SENSOR_TYPE_FP_OTHER,
142                     gestureAvailabilityDispatcher);
143             mInternalCallback = new TestableInternalCallback();
144         }
145 
146         class TestableInternalCallback extends InternalCallback {
147             @Override
onClientStarted(BaseClientMonitor clientMonitor)148             public void onClientStarted(BaseClientMonitor clientMonitor) {
149                 super.onClientStarted(clientMonitor);
150                 Slog.d(TAG, "Client started: " + clientMonitor);
151                 mFingerprint21.setDebugMessage("Started: " + clientMonitor);
152             }
153 
154             @Override
onClientFinished(BaseClientMonitor clientMonitor, boolean success)155             public void onClientFinished(BaseClientMonitor clientMonitor, boolean success) {
156                 super.onClientFinished(clientMonitor, success);
157                 Slog.d(TAG, "Client finished: " + clientMonitor);
158                 mFingerprint21.setDebugMessage("Finished: " + clientMonitor);
159             }
160         }
161 
init(@onNull Fingerprint21UdfpsMock fingerprint21)162         void init(@NonNull Fingerprint21UdfpsMock fingerprint21) {
163             mFingerprint21 = fingerprint21;
164         }
165 
166         /**
167          * Expose the internal finish callback so it can be used for testing
168          */
169         @Override
getInternalCallback()170         @NonNull protected InternalCallback getInternalCallback() {
171             return mInternalCallback;
172         }
173     }
174 
175     /**
176      * All of the mocking/testing should happen in here. This way we don't need to modify the
177      * {@link BaseClientMonitor} implementations and can run the
178      * real path there.
179      */
180     private static class MockHalResultController extends HalResultController {
181 
182         // Duration for which a sensor authentication can be treated as UDFPS success.
183         private static final int AUTH_VALIDITY_MS = 10 * 1000; // 10 seconds
184 
185         static class LastAuthArgs {
186             @NonNull final AuthenticationConsumer lastAuthenticatedClient;
187             final long deviceId;
188             final int fingerId;
189             final int groupId;
190             @Nullable final ArrayList<Byte> token;
191 
LastAuthArgs(@onNull AuthenticationConsumer authenticationConsumer, long deviceId, int fingerId, int groupId, @Nullable ArrayList<Byte> token)192             LastAuthArgs(@NonNull AuthenticationConsumer authenticationConsumer, long deviceId,
193                     int fingerId, int groupId, @Nullable ArrayList<Byte> token) {
194                 lastAuthenticatedClient = authenticationConsumer;
195                 this.deviceId = deviceId;
196                 this.fingerId = fingerId;
197                 this.groupId = groupId;
198                 if (token == null) {
199                     this.token = null;
200                 } else {
201                     // Store a copy so the owner can be GC'd
202                     this.token = new ArrayList<>(token);
203                 }
204             }
205         }
206 
207         // Initialized after the constructor, but before it's ever used.
208         @NonNull private RestartAuthRunnable mRestartAuthRunnable;
209         @NonNull private Fingerprint21UdfpsMock mFingerprint21;
210         @Nullable private LastAuthArgs mLastAuthArgs;
211 
MockHalResultController(int sensorId, @NonNull Context context, @NonNull Handler handler, @NonNull BiometricScheduler scheduler)212         MockHalResultController(int sensorId, @NonNull Context context, @NonNull Handler handler,
213                 @NonNull BiometricScheduler scheduler) {
214             super(sensorId, context, handler, scheduler);
215         }
216 
init(@onNull RestartAuthRunnable restartAuthRunnable, @NonNull Fingerprint21UdfpsMock fingerprint21)217         void init(@NonNull RestartAuthRunnable restartAuthRunnable,
218                 @NonNull Fingerprint21UdfpsMock fingerprint21) {
219             mRestartAuthRunnable = restartAuthRunnable;
220             mFingerprint21 = fingerprint21;
221         }
222 
getLastAuthenticatedClient()223         @Nullable AuthenticationConsumer getLastAuthenticatedClient() {
224             return mLastAuthArgs != null ? mLastAuthArgs.lastAuthenticatedClient : null;
225         }
226 
227         /**
228          * Intercepts the HAL authentication and holds it until the UDFPS simulation decides
229          * that authentication finished.
230          */
231         @Override
onAuthenticated(long deviceId, int fingerId, int groupId, ArrayList<Byte> token)232         public void onAuthenticated(long deviceId, int fingerId, int groupId,
233                 ArrayList<Byte> token) {
234             mHandler.post(() -> {
235                 final BaseClientMonitor client = mScheduler.getCurrentClient();
236                 if (!(client instanceof AuthenticationConsumer)) {
237                     Slog.e(TAG, "Non authentication consumer: " + client);
238                     return;
239                 }
240 
241                 final boolean accepted = fingerId != 0;
242                 if (accepted) {
243                     mFingerprint21.setDebugMessage("Finger accepted");
244                 } else {
245                     mFingerprint21.setDebugMessage("Finger rejected");
246                 }
247 
248                 final AuthenticationConsumer authenticationConsumer =
249                         (AuthenticationConsumer) client;
250                 mLastAuthArgs = new LastAuthArgs(authenticationConsumer, deviceId, fingerId,
251                         groupId, token);
252 
253                 // Remove any existing restart runnbables since auth just started, and put a new
254                 // one on the queue.
255                 mHandler.removeCallbacks(mRestartAuthRunnable);
256                 mRestartAuthRunnable.setLastAuthReference(authenticationConsumer);
257                 mHandler.postDelayed(mRestartAuthRunnable, AUTH_VALIDITY_MS);
258             });
259         }
260 
261         /**
262          * Calls through to the real interface and notifies clients of accept/reject.
263          */
sendAuthenticated(long deviceId, int fingerId, int groupId, ArrayList<Byte> token)264         void sendAuthenticated(long deviceId, int fingerId, int groupId,
265                 ArrayList<Byte> token) {
266             Slog.d(TAG, "sendAuthenticated: " + (fingerId != 0));
267             mFingerprint21.setDebugMessage("Udfps match: " + (fingerId != 0));
268             super.onAuthenticated(deviceId, fingerId, groupId, token);
269         }
270     }
271 
newInstance(@onNull Context context, @NonNull FingerprintSensorPropertiesInternal sensorProps, @NonNull LockoutResetDispatcher lockoutResetDispatcher, @NonNull GestureAvailabilityDispatcher gestureAvailabilityDispatcher)272     public static Fingerprint21UdfpsMock newInstance(@NonNull Context context,
273             @NonNull FingerprintSensorPropertiesInternal sensorProps,
274             @NonNull LockoutResetDispatcher lockoutResetDispatcher,
275             @NonNull GestureAvailabilityDispatcher gestureAvailabilityDispatcher) {
276         Slog.d(TAG, "Creating Fingerprint23Mock!");
277 
278         final Handler handler = new Handler(Looper.getMainLooper());
279         final TestableBiometricScheduler scheduler =
280                 new TestableBiometricScheduler(TAG, gestureAvailabilityDispatcher);
281         final MockHalResultController controller =
282                 new MockHalResultController(sensorProps.sensorId, context, handler, scheduler);
283         return new Fingerprint21UdfpsMock(context, sensorProps, scheduler, handler,
284                 lockoutResetDispatcher, controller);
285     }
286 
287     private static abstract class FakeFingerRunnable implements Runnable {
288         private long mFingerDownTime;
289         private int mCaptureDuration;
290 
291         /**
292          * @param fingerDownTime System time when onFingerDown occurred
293          * @param captureDuration Duration that the finger needs to be down for
294          */
setSimulationTime(long fingerDownTime, int captureDuration)295         void setSimulationTime(long fingerDownTime, int captureDuration) {
296             mFingerDownTime = fingerDownTime;
297             mCaptureDuration = captureDuration;
298         }
299 
300         @SuppressWarnings("BooleanMethodIsAlwaysInverted")
isImageCaptureComplete()301         boolean isImageCaptureComplete() {
302             return System.currentTimeMillis() - mFingerDownTime > mCaptureDuration;
303         }
304     }
305 
306     private final class FakeRejectRunnable extends FakeFingerRunnable {
307         @Override
run()308         public void run() {
309             mMockHalResultController.sendAuthenticated(0, 0, 0, null);
310         }
311     }
312 
313     private final class FakeAcceptRunnable extends FakeFingerRunnable {
314         @Override
run()315         public void run() {
316             if (mMockHalResultController.mLastAuthArgs == null) {
317                 // This can happen if the user has trust agents enabled, which make lockscreen
318                 // dismissable. Send a fake non-zero (accept) finger.
319                 Slog.d(TAG, "Sending fake finger");
320                 mMockHalResultController.sendAuthenticated(1 /* deviceId */,
321                         1 /* fingerId */, 1 /* groupId */, null /* token */);
322             } else {
323                 mMockHalResultController.sendAuthenticated(
324                         mMockHalResultController.mLastAuthArgs.deviceId,
325                         mMockHalResultController.mLastAuthArgs.fingerId,
326                         mMockHalResultController.mLastAuthArgs.groupId,
327                         mMockHalResultController.mLastAuthArgs.token);
328             }
329         }
330     }
331 
332     /**
333      * The fingerprint HAL allows multiple (5) fingerprint attempts per HIDL invocation of the
334      * authenticate method. However, valid fingerprint authentications are invalidated after
335      * {@link MockHalResultController#AUTH_VALIDITY_MS}, meaning UDFPS touches will be reported as
336      * rejects if touched after that duration. However, since a valid fingerprint was detected, the
337      * HAL and FingerprintService will not look for subsequent fingerprints.
338      *
339      * In order to keep the FingerprintManager API consistent (that multiple fingerprint attempts
340      * are allowed per auth lifecycle), we internally cancel and restart authentication so that the
341      * sensor is responsive again.
342      */
343     private static final class RestartAuthRunnable implements Runnable {
344         @NonNull private final Fingerprint21UdfpsMock mFingerprint21;
345         @NonNull private final TestableBiometricScheduler mScheduler;
346 
347         // Store a reference to the auth consumer that should be invalidated.
348         private AuthenticationConsumer mLastAuthConsumer;
349 
RestartAuthRunnable(@onNull Fingerprint21UdfpsMock fingerprint21, @NonNull TestableBiometricScheduler scheduler)350         RestartAuthRunnable(@NonNull Fingerprint21UdfpsMock fingerprint21,
351                 @NonNull TestableBiometricScheduler scheduler) {
352             mFingerprint21 = fingerprint21;
353             mScheduler = scheduler;
354         }
355 
setLastAuthReference(AuthenticationConsumer lastAuthConsumer)356         void setLastAuthReference(AuthenticationConsumer lastAuthConsumer) {
357             mLastAuthConsumer = lastAuthConsumer;
358         }
359 
360         @Override
run()361         public void run() {
362             final BaseClientMonitor client = mScheduler.getCurrentClient();
363 
364             // We don't care about FingerprintDetectClient, since accept/rejects are both OK. UDFPS
365             // rejects will just simulate the path where non-enrolled fingers are presented.
366             if (!(client instanceof FingerprintAuthenticationClient)) {
367                 Slog.e(TAG, "Non-FingerprintAuthenticationClient client: " + client);
368                 return;
369             }
370 
371             // Perhaps the runnable is stale, or the user stopped/started auth manually. Do not
372             // restart auth in this case.
373             if (client != mLastAuthConsumer) {
374                 Slog.e(TAG, "Current client: " + client
375                         + " does not match mLastAuthConsumer: " + mLastAuthConsumer);
376                 return;
377             }
378 
379             Slog.d(TAG, "Restarting auth, current: " + client);
380             mFingerprint21.setDebugMessage("Auth timed out");
381 
382             final FingerprintAuthenticationClient authClient =
383                     (FingerprintAuthenticationClient) client;
384             // Store the authClient parameters so it can be rescheduled
385             final IBinder token = client.getToken();
386             final long operationId = authClient.getOperationId();
387             final int user = client.getTargetUserId();
388             final int cookie = client.getCookie();
389             final ClientMonitorCallbackConverter listener = client.getListener();
390             final String opPackageName = client.getOwnerString();
391             final boolean restricted = authClient.isRestricted();
392             final int statsClient = client.getStatsClient();
393             final boolean isKeyguard = authClient.isKeyguard();
394 
395             // Don't actually send cancel() to the HAL, since successful auth already finishes
396             // HAL authenticate() lifecycle. Just
397             mScheduler.getInternalCallback().onClientFinished(client, true /* success */);
398 
399             // Schedule this only after we invoke onClientFinished for the previous client, so that
400             // internal preemption logic is not run.
401             mFingerprint21.scheduleAuthenticate(mFingerprint21.mSensorProperties.sensorId, token,
402                     operationId, user, cookie, listener, opPackageName, restricted, statsClient,
403                     isKeyguard, null /* fingerprintStateCallback */);
404         }
405     }
406 
Fingerprint21UdfpsMock(@onNull Context context, @NonNull FingerprintSensorPropertiesInternal sensorProps, @NonNull TestableBiometricScheduler scheduler, @NonNull Handler handler, @NonNull LockoutResetDispatcher lockoutResetDispatcher, @NonNull MockHalResultController controller)407     private Fingerprint21UdfpsMock(@NonNull Context context,
408             @NonNull FingerprintSensorPropertiesInternal sensorProps,
409             @NonNull TestableBiometricScheduler scheduler,
410             @NonNull Handler handler,
411             @NonNull LockoutResetDispatcher lockoutResetDispatcher,
412             @NonNull MockHalResultController controller) {
413         super(context, sensorProps, scheduler, handler, lockoutResetDispatcher, controller);
414         mScheduler = scheduler;
415         mScheduler.init(this);
416         mHandler = handler;
417         // resetLockout is controlled by the framework, so hardwareAuthToken is not required
418         final boolean resetLockoutRequiresHardwareAuthToken = false;
419         final int maxTemplatesAllowed = mContext.getResources()
420                 .getInteger(R.integer.config_fingerprintMaxTemplatesPerUser);
421         mSensorProperties = new FingerprintSensorPropertiesInternal(sensorProps.sensorId,
422                 sensorProps.sensorStrength, maxTemplatesAllowed, sensorProps.componentInfo,
423                 FingerprintSensorProperties.TYPE_UDFPS_OPTICAL,
424                 resetLockoutRequiresHardwareAuthToken, sensorProps.sensorLocationX,
425                 sensorProps.sensorLocationY, sensorProps.sensorRadius);
426         mMockHalResultController = controller;
427         mUserHasTrust = new SparseBooleanArray();
428         mTrustManager = context.getSystemService(TrustManager.class);
429         mTrustManager.registerTrustListener(this);
430         mRandom = new Random();
431         mFakeRejectRunnable = new FakeRejectRunnable();
432         mFakeAcceptRunnable = new FakeAcceptRunnable();
433         mRestartAuthRunnable = new RestartAuthRunnable(this, mScheduler);
434 
435         // We can't initialize this during MockHalresultController's constructor due to a circular
436         // dependency.
437         mMockHalResultController.init(mRestartAuthRunnable, this);
438     }
439 
440     @Override
onTrustChanged(boolean enabled, int userId, int flags)441     public void onTrustChanged(boolean enabled, int userId, int flags) {
442         mUserHasTrust.put(userId, enabled);
443     }
444 
445     @Override
onTrustManagedChanged(boolean enabled, int userId)446     public void onTrustManagedChanged(boolean enabled, int userId) {
447 
448     }
449 
450     @Override
onTrustError(CharSequence message)451     public void onTrustError(CharSequence message) {
452 
453     }
454 
455     @Override
456     @NonNull
getSensorProperties()457     public List<FingerprintSensorPropertiesInternal> getSensorProperties() {
458         final List<FingerprintSensorPropertiesInternal> properties = new ArrayList<>();
459         properties.add(mSensorProperties);
460         return properties;
461     }
462 
463     @Override
onPointerDown(int sensorId, int x, int y, float minor, float major)464     public void onPointerDown(int sensorId, int x, int y, float minor, float major) {
465         mHandler.post(() -> {
466             Slog.d(TAG, "onFingerDown");
467             final AuthenticationConsumer lastAuthenticatedConsumer =
468                     mMockHalResultController.getLastAuthenticatedClient();
469             final BaseClientMonitor currentScheduledClient = mScheduler.getCurrentClient();
470 
471             if (currentScheduledClient == null) {
472                 Slog.d(TAG, "Not authenticating");
473                 return;
474             }
475 
476             mHandler.removeCallbacks(mFakeRejectRunnable);
477             mHandler.removeCallbacks(mFakeAcceptRunnable);
478 
479             // The sensor was authenticated, is still the currently scheduled client, and the
480             // user touched the UDFPS affordance. Pretend that auth succeeded.
481             final boolean authenticatedClientIsCurrent = lastAuthenticatedConsumer != null
482                     && lastAuthenticatedConsumer == currentScheduledClient;
483             // User is unlocked on keyguard via Trust Agent
484             final boolean keyguardAndTrusted;
485             if (currentScheduledClient instanceof FingerprintAuthenticationClient) {
486                 keyguardAndTrusted = ((FingerprintAuthenticationClient) currentScheduledClient)
487                         .isKeyguard()
488                         && mUserHasTrust.get(currentScheduledClient.getTargetUserId(), false);
489             } else {
490                 keyguardAndTrusted = false;
491             }
492 
493             final int captureDuration = getNewCaptureDuration();
494             final int matchingDuration = getMatchingDuration();
495             final int totalDuration = captureDuration + matchingDuration;
496             setDebugMessage("Duration: " + totalDuration
497                     + " (" + captureDuration + " + " + matchingDuration + ")");
498             if (authenticatedClientIsCurrent || keyguardAndTrusted) {
499                 mFakeAcceptRunnable.setSimulationTime(System.currentTimeMillis(), captureDuration);
500                 mHandler.postDelayed(mFakeAcceptRunnable, totalDuration);
501             } else if (currentScheduledClient instanceof AuthenticationConsumer) {
502                 // Something is authenticating but authentication has not succeeded yet. Pretend
503                 // that auth rejected.
504                 mFakeRejectRunnable.setSimulationTime(System.currentTimeMillis(), captureDuration);
505                 mHandler.postDelayed(mFakeRejectRunnable, totalDuration);
506             }
507         });
508     }
509 
510     @Override
onPointerUp(int sensorId)511     public void onPointerUp(int sensorId) {
512         mHandler.post(() -> {
513             Slog.d(TAG, "onFingerUp");
514 
515             // Only one of these can be on the handler at any given time (see onFingerDown). If
516             // image capture is not complete, send ACQUIRED_TOO_FAST and remove the runnable from
517             // the handler. Image capture (onFingerDown) needs to happen again.
518             if (mHandler.hasCallbacks(mFakeRejectRunnable)
519                     && !mFakeRejectRunnable.isImageCaptureComplete()) {
520                 mHandler.removeCallbacks(mFakeRejectRunnable);
521                 mMockHalResultController.onAcquired(0 /* deviceId */,
522                         FingerprintManager.FINGERPRINT_ACQUIRED_TOO_FAST,
523                         0 /* vendorCode */);
524             } else if (mHandler.hasCallbacks(mFakeAcceptRunnable)
525                     && !mFakeAcceptRunnable.isImageCaptureComplete()) {
526                 mHandler.removeCallbacks(mFakeAcceptRunnable);
527                 mMockHalResultController.onAcquired(0 /* deviceId */,
528                         FingerprintManager.FINGERPRINT_ACQUIRED_TOO_FAST,
529                         0 /* vendorCode */);
530             }
531         });
532     }
533 
getNewCaptureDuration()534     private int getNewCaptureDuration() {
535         final ContentResolver contentResolver = mContext.getContentResolver();
536         final int captureTime = Settings.Secure.getIntForUser(contentResolver,
537                 CONFIG_AUTH_DELAY_PT1,
538                 DEFAULT_AUTH_DELAY_PT1_MS,
539                 UserHandle.USER_CURRENT);
540         final int randomDelayRange = Settings.Secure.getIntForUser(contentResolver,
541                 CONFIG_AUTH_DELAY_RANDOMNESS,
542                 DEFAULT_AUTH_DELAY_RANDOMNESS_MS,
543                 UserHandle.USER_CURRENT);
544         final int randomDelay = mRandom.nextInt(randomDelayRange * 2) - randomDelayRange;
545 
546         // Must be at least 0
547         return Math.max(captureTime + randomDelay, 0);
548     }
549 
getMatchingDuration()550     private int getMatchingDuration() {
551         final int matchingTime = Settings.Secure.getIntForUser(mContext.getContentResolver(),
552                 CONFIG_AUTH_DELAY_PT2,
553                 DEFAULT_AUTH_DELAY_PT2_MS,
554                 UserHandle.USER_CURRENT);
555 
556         // Must be at least 0
557         return Math.max(matchingTime, 0);
558     }
559 
setDebugMessage(String message)560     private void setDebugMessage(String message) {
561         try {
562             final IUdfpsOverlayController controller = getUdfpsOverlayController();
563             // Things can happen before SysUI loads and sets the controller.
564             if (controller != null) {
565                 Slog.d(TAG, "setDebugMessage: " + message);
566                 controller.setDebugMessage(mSensorProperties.sensorId, message);
567             }
568         } catch (RemoteException e) {
569             Slog.e(TAG, "Remote exception when sending message: " + message, e);
570         }
571     }
572 }
573