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