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.systemui.biometrics;
18 
19 import static android.app.StatusBarManager.SESSION_BIOMETRIC_PROMPT;
20 import static android.app.StatusBarManager.SESSION_KEYGUARD;
21 import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_GOOD;
22 import static android.hardware.biometrics.BiometricRequestConstants.REASON_AUTH_BP;
23 import static android.hardware.biometrics.BiometricRequestConstants.REASON_AUTH_KEYGUARD;
24 import static android.hardware.biometrics.BiometricRequestConstants.REASON_ENROLL_ENROLLING;
25 import static android.hardware.biometrics.BiometricRequestConstants.REASON_ENROLL_FIND_SENSOR;
26 
27 import static com.android.internal.util.LatencyTracker.ACTION_UDFPS_ILLUMINATE;
28 import static com.android.internal.util.Preconditions.checkNotNull;
29 import static com.android.systemui.classifier.Classifier.UDFPS_AUTHENTICATION;
30 
31 import android.content.BroadcastReceiver;
32 import android.content.Context;
33 import android.content.Intent;
34 import android.content.IntentFilter;
35 import android.graphics.Rect;
36 import android.hardware.biometrics.BiometricFingerprintConstants;
37 import android.hardware.biometrics.BiometricPrompt;
38 import android.hardware.biometrics.SensorProperties;
39 import android.hardware.display.DisplayManager;
40 import android.hardware.fingerprint.FingerprintManager;
41 import android.hardware.fingerprint.FingerprintSensorProperties;
42 import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
43 import android.hardware.fingerprint.IUdfpsOverlayController;
44 import android.hardware.fingerprint.IUdfpsOverlayControllerCallback;
45 import android.hardware.input.InputManager;
46 import android.os.Build;
47 import android.os.CancellationSignal;
48 import android.os.Handler;
49 import android.os.Looper;
50 import android.os.PowerManager;
51 import android.os.Trace;
52 import android.os.VibrationAttributes;
53 import android.os.VibrationEffect;
54 import android.util.Log;
55 import android.view.HapticFeedbackConstants;
56 import android.view.LayoutInflater;
57 import android.view.MotionEvent;
58 import android.view.View;
59 import android.view.WindowManager;
60 import android.view.accessibility.AccessibilityManager;
61 
62 import androidx.annotation.NonNull;
63 import androidx.annotation.Nullable;
64 import androidx.annotation.OptIn;
65 
66 import com.android.internal.R;
67 import com.android.internal.annotations.VisibleForTesting;
68 import com.android.internal.logging.InstanceId;
69 import com.android.internal.util.LatencyTracker;
70 import com.android.keyguard.KeyguardUpdateMonitor;
71 import com.android.systemui.Dumpable;
72 import com.android.systemui.animation.ActivityTransitionAnimator;
73 import com.android.systemui.biometrics.dagger.BiometricsBackground;
74 import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor;
75 import com.android.systemui.biometrics.shared.model.UdfpsOverlayParams;
76 import com.android.systemui.biometrics.udfps.InteractionEvent;
77 import com.android.systemui.biometrics.udfps.NormalizedTouchData;
78 import com.android.systemui.biometrics.udfps.SinglePointerTouchProcessor;
79 import com.android.systemui.biometrics.udfps.TouchProcessor;
80 import com.android.systemui.biometrics.udfps.TouchProcessorResult;
81 import com.android.systemui.biometrics.ui.viewmodel.DefaultUdfpsTouchOverlayViewModel;
82 import com.android.systemui.biometrics.ui.viewmodel.DeviceEntryUdfpsTouchOverlayViewModel;
83 import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor;
84 import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor;
85 import com.android.systemui.dagger.SysUISingleton;
86 import com.android.systemui.dagger.qualifiers.Application;
87 import com.android.systemui.dagger.qualifiers.Main;
88 import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor;
89 import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor;
90 import com.android.systemui.doze.DozeReceiver;
91 import com.android.systemui.dump.DumpManager;
92 import com.android.systemui.keyguard.ScreenLifecycle;
93 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
94 import com.android.systemui.log.SessionTracker;
95 import com.android.systemui.plugins.FalsingManager;
96 import com.android.systemui.plugins.statusbar.StatusBarStateController;
97 import com.android.systemui.power.domain.interactor.PowerInteractor;
98 import com.android.systemui.shade.domain.interactor.ShadeInteractor;
99 import com.android.systemui.shared.system.SysUiStatsLog;
100 import com.android.systemui.statusbar.LockscreenShadeTransitionController;
101 import com.android.systemui.statusbar.VibratorHelper;
102 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
103 import com.android.systemui.statusbar.phone.SystemUIDialogManager;
104 import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController;
105 import com.android.systemui.statusbar.policy.ConfigurationController;
106 import com.android.systemui.statusbar.policy.KeyguardStateController;
107 import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
108 import com.android.systemui.util.concurrency.DelayableExecutor;
109 import com.android.systemui.util.concurrency.Execution;
110 import com.android.systemui.util.time.SystemClock;
111 
112 import dagger.Lazy;
113 
114 import kotlin.Unit;
115 
116 import kotlinx.coroutines.CoroutineScope;
117 import kotlinx.coroutines.ExperimentalCoroutinesApi;
118 
119 import java.io.PrintWriter;
120 import java.util.ArrayList;
121 import java.util.HashSet;
122 import java.util.Set;
123 import java.util.concurrent.Executor;
124 
125 import javax.inject.Inject;
126 
127 /**
128  * Shows and hides the under-display fingerprint sensor (UDFPS) overlay, handles UDFPS touch events,
129  * and toggles the UDFPS display mode.
130  *
131  * Note that the current architecture is designed so that a single {@link UdfpsController}
132  * controls/manages all UDFPS sensors. In other words, a single controller is registered with
133  * {@link com.android.server.biometrics.sensors.fingerprint.FingerprintService}, and interfaces such
134  * as {@link FingerprintManager#onPointerDown(long, int, int, int, float, float)} or
135  * {@link IUdfpsOverlayController#showUdfpsOverlay} should all have
136  * {@code sensorId} parameters.
137  */
138 @SuppressWarnings("deprecation")
139 @SysUISingleton
140 public class UdfpsController implements DozeReceiver, Dumpable {
141     private static final String TAG = "UdfpsController";
142     private static final long AOD_SEND_FINGER_UP_DELAY_MILLIS = 1000;
143 
144     private static final long MIN_UNCHANGED_INTERACTION_LOG_INTERVAL = 50;
145 
146     private final Context mContext;
147     private final Execution mExecution;
148     private final FingerprintManager mFingerprintManager;
149     @NonNull private final LayoutInflater mInflater;
150     private final WindowManager mWindowManager;
151     private final DelayableExecutor mFgExecutor;
152     @NonNull private final Executor mBiometricExecutor;
153     @NonNull private final StatusBarStateController mStatusBarStateController;
154     @NonNull private final KeyguardStateController mKeyguardStateController;
155     @NonNull private final StatusBarKeyguardViewManager mKeyguardViewManager;
156     @NonNull private final DumpManager mDumpManager;
157     @NonNull private final SystemUIDialogManager mDialogManager;
158     @NonNull private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
159     @NonNull private final DeviceEntryFaceAuthInteractor mDeviceEntryFaceAuthInteractor;
160     @NonNull private final VibratorHelper mVibrator;
161     @NonNull private final FalsingManager mFalsingManager;
162     @NonNull private final PowerManager mPowerManager;
163     @NonNull private final AccessibilityManager mAccessibilityManager;
164     @NonNull private final LockscreenShadeTransitionController mLockscreenShadeTransitionController;
165     @NonNull private final ConfigurationController mConfigurationController;
166     @NonNull private final SystemClock mSystemClock;
167     @NonNull private final UnlockedScreenOffAnimationController
168             mUnlockedScreenOffAnimationController;
169     @NonNull private final LatencyTracker mLatencyTracker;
170     @VisibleForTesting @NonNull final BiometricDisplayListener mOrientationListener;
171     @NonNull private final ActivityTransitionAnimator mActivityTransitionAnimator;
172     @NonNull private final PrimaryBouncerInteractor mPrimaryBouncerInteractor;
173     @NonNull private final ShadeInteractor mShadeInteractor;
174     @Nullable private final TouchProcessor mTouchProcessor;
175     @NonNull private final SessionTracker mSessionTracker;
176     @NonNull private final Lazy<DeviceEntryUdfpsTouchOverlayViewModel>
177             mDeviceEntryUdfpsTouchOverlayViewModel;
178     @NonNull private final Lazy<DefaultUdfpsTouchOverlayViewModel>
179             mDefaultUdfpsTouchOverlayViewModel;
180     @NonNull private final AlternateBouncerInteractor mAlternateBouncerInteractor;
181     @NonNull private final UdfpsOverlayInteractor mUdfpsOverlayInteractor;
182     @NonNull private final PowerInteractor mPowerInteractor;
183     @NonNull private final CoroutineScope mScope;
184     @NonNull private final InputManager mInputManager;
185     @NonNull private final UdfpsKeyguardAccessibilityDelegate mUdfpsKeyguardAccessibilityDelegate;
186     @NonNull private final SelectedUserInteractor mSelectedUserInteractor;
187     private final boolean mIgnoreRefreshRate;
188     private final KeyguardTransitionInteractor mKeyguardTransitionInteractor;
189 
190     // Currently the UdfpsController supports a single UDFPS sensor. If devices have multiple
191     // sensors, this, in addition to a lot of the code here, will be updated.
192     @VisibleForTesting @NonNull FingerprintSensorPropertiesInternal mSensorProps;
193     @VisibleForTesting @NonNull UdfpsOverlayParams mOverlayParams = new UdfpsOverlayParams();
194     // TODO(b/229290039): UDFPS controller should manage its dimensions on its own. Remove this.
195     @Nullable private Runnable mAuthControllerUpdateUdfpsLocation;
196     @Nullable private UdfpsDisplayModeProvider mUdfpsDisplayMode;
197 
198     // The ID of the pointer for which ACTION_DOWN has occurred. -1 means no pointer is active.
199     private int mActivePointerId = MotionEvent.INVALID_POINTER_ID;
200     // Whether a pointer has been pilfered for current gesture
201     private boolean mPointerPilfered = false;
202     // The timestamp of the most recent touch log.
203     private long mTouchLogTime;
204     // The timestamp of the most recent log of a touch InteractionEvent.
205     private long mLastTouchInteractionTime;
206     // Sensor has a capture (good or bad) for this touch. No need to enable the UDFPS display mode
207     // anymore for this particular touch event. In other words, do not enable the UDFPS mode until
208     // the user touches the sensor area again.
209     private boolean mAcquiredReceived;
210 
211     // The current request from FingerprintService. Null if no current request.
212     @Nullable UdfpsControllerOverlay mOverlay;
213 
214     // The fingerprint AOD trigger doesn't provide an ACTION_UP/ACTION_CANCEL event to tell us when
215     // to turn off high brightness mode. To get around this limitation, the state of the AOD
216     // interrupt is being tracked and a timeout is used as a last resort to turn off high brightness
217     // mode.
218     private boolean mIsAodInterruptActive;
219     @Nullable private Runnable mCancelAodFingerUpAction;
220     private boolean mScreenOn;
221     private Runnable mAodInterruptRunnable;
222     private boolean mOnFingerDown;
223     private boolean mAttemptedToDismissKeyguard;
224     private final Set<Callback> mCallbacks = new HashSet<>();
225 
226     @VisibleForTesting
227     public static final VibrationAttributes UDFPS_VIBRATION_ATTRIBUTES =
228             new VibrationAttributes.Builder()
229                     // vibration will bypass battery saver mode:
230                     .setUsage(VibrationAttributes.USAGE_COMMUNICATION_REQUEST)
231                     .build();
232     @VisibleForTesting
233     public static final VibrationAttributes LOCK_ICON_VIBRATION_ATTRIBUTES =
234             new VibrationAttributes.Builder()
235                     .setUsage(VibrationAttributes.USAGE_TOUCH)
236                     .build();
237 
238     // haptic to use for successful device entry
239     public static final VibrationEffect EFFECT_CLICK =
240             VibrationEffect.get(VibrationEffect.EFFECT_CLICK);
241 
242     public static final int LONG_PRESS = HapticFeedbackConstants.LONG_PRESS;
243 
244     private final ScreenLifecycle.Observer mScreenObserver = new ScreenLifecycle.Observer() {
245         @Override
246         public void onScreenTurnedOn() {
247             mScreenOn = true;
248             if (mAodInterruptRunnable != null) {
249                 mAodInterruptRunnable.run();
250                 mAodInterruptRunnable = null;
251             }
252         }
253 
254         @Override
255         public void onScreenTurnedOff() {
256             mScreenOn = false;
257         }
258     };
259 
260     @Override
dump(@onNull PrintWriter pw, @NonNull String[] args)261     public void dump(@NonNull PrintWriter pw, @NonNull String[] args) {
262         final int touchConfigId = mContext.getResources().getInteger(
263                 com.android.internal.R.integer.config_selected_udfps_touch_detection);
264         pw.println("mSensorProps=(" + mSensorProps + ")");
265         pw.println("touchConfigId: " + touchConfigId);
266     }
267 
268     public class UdfpsOverlayController extends IUdfpsOverlayController.Stub {
269         @OptIn(markerClass = ExperimentalCoroutinesApi.class)
270         @Override
showUdfpsOverlay(long requestId, int sensorId, int reason, @NonNull IUdfpsOverlayControllerCallback callback)271         public void showUdfpsOverlay(long requestId, int sensorId, int reason,
272                 @NonNull IUdfpsOverlayControllerCallback callback) {
273             mUdfpsOverlayInteractor.setRequestId(requestId);
274             mFgExecutor.execute(() -> UdfpsController.this.showUdfpsOverlay(
275                     new UdfpsControllerOverlay(
276                         mContext,
277                         mInflater,
278                         mWindowManager,
279                         mAccessibilityManager,
280                         mStatusBarStateController,
281                         mKeyguardViewManager,
282                         mKeyguardUpdateMonitor,
283                         mDialogManager,
284                         mDumpManager,
285                         mLockscreenShadeTransitionController,
286                         mConfigurationController,
287                         mKeyguardStateController,
288                         mUnlockedScreenOffAnimationController,
289                         mUdfpsDisplayMode,
290                         requestId,
291                         reason,
292                         callback,
293                         (view, event, fromUdfpsView) -> onTouch(
294                             requestId,
295                             event,
296                             fromUdfpsView
297                         ),
298                             mActivityTransitionAnimator,
299                         mPrimaryBouncerInteractor,
300                         mAlternateBouncerInteractor,
301                         mUdfpsKeyguardAccessibilityDelegate,
302                         mKeyguardTransitionInteractor,
303                         mSelectedUserInteractor,
304                         mDeviceEntryUdfpsTouchOverlayViewModel,
305                         mDefaultUdfpsTouchOverlayViewModel,
306                         mShadeInteractor,
307                         mUdfpsOverlayInteractor,
308                         mPowerInteractor,
309                         mScope
310                     )));
311         }
312 
313         @Override
hideUdfpsOverlay(int sensorId)314         public void hideUdfpsOverlay(int sensorId) {
315             mFgExecutor.execute(() -> {
316                 if (mKeyguardUpdateMonitor.isFingerprintDetectionRunning()) {
317                     // if we get here, we expect keyguardUpdateMonitor's fingerprintRunningState
318                     // to be updated shortly afterwards
319                     Log.d(TAG, "hiding udfps overlay when "
320                             + "mKeyguardUpdateMonitor.isFingerprintDetectionRunning()=true");
321                 }
322 
323                 UdfpsController.this.hideUdfpsOverlay();
324             });
325         }
326 
327         @Override
onAcquired( int sensorId, @BiometricFingerprintConstants.FingerprintAcquired int acquiredInfo )328         public void onAcquired(
329                 int sensorId,
330                 @BiometricFingerprintConstants.FingerprintAcquired int acquiredInfo
331         ) {
332             if (BiometricFingerprintConstants.shouldDisableUdfpsDisplayMode(acquiredInfo)) {
333                 boolean acquiredGood = acquiredInfo == FINGERPRINT_ACQUIRED_GOOD;
334                 mFgExecutor.execute(() -> {
335                     if (mOverlay == null) {
336                         Log.e(TAG, "Null request when onAcquired for sensorId: " + sensorId
337                                 + " acquiredInfo=" + acquiredInfo);
338                         return;
339                     }
340                     mAcquiredReceived = true;
341                     final View view = mOverlay.getTouchOverlay();
342                     unconfigureDisplay(view);
343                     tryAodSendFingerUp();
344                 });
345             }
346         }
347 
348         @Override
onEnrollmentProgress(int sensorId, int remaining)349         public void onEnrollmentProgress(int sensorId, int remaining) { }
350 
351         @Override
onEnrollmentHelp(int sensorId)352         public void onEnrollmentHelp(int sensorId) { }
353 
354         @Override
setDebugMessage(int sensorId, String message)355         public void setDebugMessage(int sensorId, String message) {
356             mFgExecutor.execute(() -> {
357                 if (mOverlay == null || mOverlay.isHiding()) {
358                     return;
359                 }
360                 if (!DeviceEntryUdfpsRefactor.isEnabled()) {
361                     ((UdfpsView) mOverlay.getTouchOverlay()).setDebugMessage(message);
362                 }
363             });
364         }
365 
getSensorBounds()366         public Rect getSensorBounds() {
367             return mOverlayParams.getSensorBounds();
368         }
369 
370         /**
371          * Passes a mocked MotionEvent to OnTouch.
372          *
373          * @param event MotionEvent to simulate in onTouch
374          */
debugOnTouch(MotionEvent event)375         public void debugOnTouch(MotionEvent event) {
376             final long requestId = (mOverlay != null) ? mOverlay.getRequestId() : 0L;
377             UdfpsController.this.onTouch(requestId, event, true);
378         }
379 
380         /**
381          * Debug to run onUiReady
382          */
debugOnUiReady(int sensorId)383         public void debugOnUiReady(int sensorId) {
384             final long requestId = (mOverlay != null) ? mOverlay.getRequestId() : 0L;
385             UdfpsController.this.mFingerprintManager.onUdfpsUiEvent(
386                     FingerprintManager.UDFPS_UI_READY, requestId, sensorId);
387         }
388 
389         /**
390          * Debug to show biometric prompt
391          */
debugBiometricPrompt()392         public void debugBiometricPrompt() {
393             final BiometricPrompt.AuthenticationCallback authenticationCallback =
394                     new BiometricPrompt.AuthenticationCallback() {
395                     };
396 
397             final BiometricPrompt biometricPrompt = new BiometricPrompt.Builder(mContext)
398                     .setTitle("Test")
399                     .setDeviceCredentialAllowed(true)
400                     .setAllowBackgroundAuthentication(true)
401                     .build();
402             final Handler handler = new Handler(Looper.getMainLooper());
403             biometricPrompt.authenticate(
404                     new CancellationSignal(),
405                     handler::post,
406                     authenticationCallback);
407         }
408 
409         /**
410          * Debug to run setIgnoreDisplayTouches
411          */
debugSetIgnoreDisplayTouches(boolean ignoreTouch)412         public void debugSetIgnoreDisplayTouches(boolean ignoreTouch) {
413             final long requestId = (mOverlay != null) ? mOverlay.getRequestId() : 0L;
414             UdfpsController.this.mFingerprintManager.setIgnoreDisplayTouches(
415                     requestId, mSensorProps.sensorId, ignoreTouch);
416         }
417     }
418 
419     /**
420      * Updates the overlay parameters and reconstructs or redraws the overlay, if necessary.
421      *
422      * @param sensorProps   sensor for which the overlay is getting updated.
423      * @param overlayParams See {@link UdfpsOverlayParams}.
424      */
updateOverlayParams(@onNull FingerprintSensorPropertiesInternal sensorProps, @NonNull UdfpsOverlayParams overlayParams)425     public void updateOverlayParams(@NonNull FingerprintSensorPropertiesInternal sensorProps,
426             @NonNull UdfpsOverlayParams overlayParams) {
427         if (mSensorProps.sensorId != sensorProps.sensorId) {
428             mSensorProps = sensorProps;
429             Log.w(TAG, "updateUdfpsParams | sensorId has changed");
430         }
431 
432         if (!mOverlayParams.equals(overlayParams)) {
433             mOverlayParams = overlayParams;
434 
435             if (DeviceEntryUdfpsRefactor.isEnabled()) {
436                 if (mOverlay != null && mOverlay.getRequestReason() == REASON_AUTH_KEYGUARD) {
437                     mOverlay.updateOverlayParams(mOverlayParams);
438                 } else {
439                     redrawOverlay();
440                 }
441             } else {
442                 final boolean wasShowingAlternateBouncer =
443                         mAlternateBouncerInteractor.isVisibleState();
444                 // When the bounds change it's always to re-create the overlay's window with new
445                 // LayoutParams. If the overlay needs to be shown, this will re-create and show the
446                 // overlay with the updated LayoutParams. Otherwise, the overlay will remain hidden.
447                 redrawOverlay();
448                 if (wasShowingAlternateBouncer) {
449                     mKeyguardViewManager.showBouncer(true);
450                 }
451             }
452         }
453     }
454 
455     // TODO(b/229290039): UDFPS controller should manage its dimensions on its own. Remove this.
setAuthControllerUpdateUdfpsLocation(@ullable Runnable r)456     public void setAuthControllerUpdateUdfpsLocation(@Nullable Runnable r) {
457         mAuthControllerUpdateUdfpsLocation = r;
458     }
459 
setUdfpsDisplayMode(@onNull UdfpsDisplayModeProvider udfpsDisplayMode)460     public void setUdfpsDisplayMode(@NonNull UdfpsDisplayModeProvider udfpsDisplayMode) {
461         mUdfpsDisplayMode = udfpsDisplayMode;
462     }
463 
464     private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
465         @Override
466         public void onReceive(Context context, Intent intent) {
467             if (mOverlay != null
468                     && mOverlay.getRequestReason() != REASON_AUTH_KEYGUARD
469                     && Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(intent.getAction())) {
470                 String reason = intent.getStringExtra("reason");
471                 reason = (reason != null) ? reason : "unknown";
472                 Log.d(TAG, "ACTION_CLOSE_SYSTEM_DIALOGS received, reason: " + reason
473                         + ", mRequestReason: " + mOverlay.getRequestReason());
474 
475                 mOverlay.cancel();
476                 hideUdfpsOverlay();
477             }
478         }
479     };
480 
tryDismissingKeyguard()481     private void tryDismissingKeyguard() {
482         if (!mOnFingerDown) {
483             playStartHaptic();
484         }
485         mKeyguardViewManager.notifyKeyguardAuthenticated(false /* primaryAuth */);
486         mAttemptedToDismissKeyguard = true;
487     }
488 
getBiometricSessionType()489     private int getBiometricSessionType() {
490         if (mOverlay == null) {
491             return -1;
492         }
493         switch (mOverlay.getRequestReason()) {
494             case REASON_AUTH_KEYGUARD:
495                 return SESSION_KEYGUARD;
496             case REASON_AUTH_BP:
497                 return SESSION_BIOMETRIC_PROMPT;
498             case REASON_ENROLL_FIND_SENSOR:
499             case REASON_ENROLL_ENROLLING:
500                 // TODO(b/255634916): create a reason for enrollment (or an "unknown" reason).
501                 return SESSION_BIOMETRIC_PROMPT << 1;
502             default:
503                 return -1;
504         }
505     }
506 
toBiometricTouchReportedTouchType(InteractionEvent event)507     private static int toBiometricTouchReportedTouchType(InteractionEvent event) {
508         switch (event) {
509             case DOWN:
510                 return SysUiStatsLog.BIOMETRIC_TOUCH_REPORTED__TOUCH_TYPE__TOUCH_TYPE_DOWN;
511             case UP:
512                 return SysUiStatsLog.BIOMETRIC_TOUCH_REPORTED__TOUCH_TYPE__TOUCH_TYPE_UP;
513             case CANCEL:
514                 return SysUiStatsLog.BIOMETRIC_TOUCH_REPORTED__TOUCH_TYPE__TOUCH_TYPE_CANCEL;
515             default:
516                 return SysUiStatsLog.BIOMETRIC_TOUCH_REPORTED__TOUCH_TYPE__TOUCH_TYPE_UNCHANGED;
517         }
518     }
519 
logBiometricTouch(InteractionEvent event, NormalizedTouchData data)520     private void logBiometricTouch(InteractionEvent event, NormalizedTouchData data) {
521         if (event == InteractionEvent.UNCHANGED) {
522             long sinceLastLog = mSystemClock.elapsedRealtime() - mLastTouchInteractionTime;
523             if (sinceLastLog < MIN_UNCHANGED_INTERACTION_LOG_INTERVAL) {
524                 return;
525             }
526         }
527         mLastTouchInteractionTime = mSystemClock.elapsedRealtime();
528 
529         final int biometricTouchReportedTouchType = toBiometricTouchReportedTouchType(event);
530         final InstanceId sessionIdProvider = mSessionTracker.getSessionId(
531                 getBiometricSessionType());
532         final int sessionId = (sessionIdProvider != null) ? sessionIdProvider.getId() : -1;
533         final int touchConfigId = mContext.getResources().getInteger(
534                 com.android.internal.R.integer.config_selected_udfps_touch_detection);
535 
536         SysUiStatsLog.write(SysUiStatsLog.BIOMETRIC_TOUCH_REPORTED, biometricTouchReportedTouchType,
537                 touchConfigId, sessionId, data.getX(), data.getY(), data.getMinor(),
538                 data.getMajor(), data.getOrientation(), data.getTime(), data.getGestureStart(),
539                 mStatusBarStateController.isDozing());
540 
541         if (Build.isDebuggable()) {
542             Log.d(TAG, data.toPrettyString(event.toString()));
543             Log.d(TAG, "sessionId: " + sessionId
544                     + ", isAod: " + mStatusBarStateController.isDozing()
545                     + ", touchConfigId: " + touchConfigId);
546         }
547     }
548 
onTouch(long requestId, @NonNull MotionEvent event, boolean fromUdfpsView)549     private boolean onTouch(long requestId, @NonNull MotionEvent event, boolean fromUdfpsView) {
550         if (!fromUdfpsView) {
551             Log.e(TAG, "ignoring the touch injected from outside of UdfpsView");
552             return false;
553         }
554         if (mOverlay == null) {
555             Log.w(TAG, "ignoring onTouch with null overlay");
556             return false;
557         }
558         if (!mOverlay.matchesRequestId(requestId)) {
559             Log.w(TAG, "ignoring stale touch event: " + requestId + " current: "
560                     + mOverlay.getRequestId());
561             return false;
562         }
563         if (event.getAction() == MotionEvent.ACTION_DOWN
564                 || event.getAction() == MotionEvent.ACTION_HOVER_ENTER) {
565             // Reset on ACTION_DOWN, start of new gesture
566             mPointerPilfered = false;
567             if (mActivePointerId != MotionEvent.INVALID_POINTER_ID) {
568                 Log.w(TAG, "onTouch down received without a preceding up");
569             }
570             mActivePointerId = MotionEvent.INVALID_POINTER_ID;
571 
572             // It's possible on some devices to get duplicate touches from both doze and the
573             // normal touch listener. Don't reset the down in this case to avoid duplicate downs
574             if (!mIsAodInterruptActive) {
575                 mOnFingerDown = false;
576             }
577         } else if (!DeviceEntryUdfpsRefactor.isEnabled()) {
578             if ((mLockscreenShadeTransitionController.getQSDragProgress() != 0f
579                     && !mAlternateBouncerInteractor.isVisibleState())
580                     || mPrimaryBouncerInteractor.isInTransit()) {
581                 Log.w(TAG, "ignoring touch due to qsDragProcess or primaryBouncerInteractor");
582                 return false;
583             }
584         }
585 
586         final TouchProcessorResult result = mTouchProcessor.processTouch(event, mActivePointerId,
587                 mOverlayParams);
588         if (result instanceof TouchProcessorResult.Failure) {
589             Log.w(TAG, ((TouchProcessorResult.Failure) result).getReason());
590             return false;
591         }
592 
593         final TouchProcessorResult.ProcessedTouch processedTouch =
594                 (TouchProcessorResult.ProcessedTouch) result;
595         final NormalizedTouchData data = processedTouch.getTouchData();
596 
597         boolean shouldPilfer = false;
598         mActivePointerId = processedTouch.getPointerOnSensorId();
599         switch (processedTouch.getEvent()) {
600             case DOWN:
601                 if (shouldTryToDismissKeyguard()) {
602                     tryDismissingKeyguard();
603                 }
604                 if (!mOnFingerDown) {
605                     onFingerDown(requestId,
606                             data.getPointerId(),
607                             data.getX(),
608                             data.getY(),
609                             data.getMinor(),
610                             data.getMajor(),
611                             data.getOrientation(),
612                             data.getTime(),
613                             data.getGestureStart(),
614                             mStatusBarStateController.isDozing());
615                 }
616 
617                 // Pilfer if valid overlap, don't allow following events to reach keyguard
618                 shouldPilfer = true;
619 
620                 // Touch is a valid UDFPS touch. Inform the falsing manager so that the touch
621                 // isn't counted against the falsing algorithm as an accidental touch.
622                 // We do this on the DOWN event instead of CANCEL/UP because the CANCEL/UP events
623                 // get sent too late to this receiver (after the actual cancel/up motions occur),
624                 // and therefore wouldn't end up being used as part of the falsing algo.
625                 mFalsingManager.isFalseTouch(UDFPS_AUTHENTICATION);
626                 break;
627 
628             case UP:
629             case CANCEL:
630                 if (InteractionEvent.CANCEL.equals(processedTouch.getEvent())) {
631                     Log.w(TAG, "This is a CANCEL event that's reported as an UP event!");
632                 }
633                 mAttemptedToDismissKeyguard = false;
634                 onFingerUp(requestId,
635                         mOverlay.getTouchOverlay(),
636                         data.getPointerId(),
637                         data.getX(),
638                         data.getY(),
639                         data.getMinor(),
640                         data.getMajor(),
641                         data.getOrientation(),
642                         data.getTime(),
643                         data.getGestureStart(),
644                         mStatusBarStateController.isDozing());
645                 break;
646 
647             case UNCHANGED:
648                 if (mActivePointerId == MotionEvent.INVALID_POINTER_ID
649                         && mAlternateBouncerInteractor.isVisibleState()) {
650                     // No pointer on sensor, forward to keyguard if alternateBouncer is visible
651                     mKeyguardViewManager.onTouch(event);
652                 }
653 
654             default:
655                 break;
656         }
657         logBiometricTouch(processedTouch.getEvent(), data);
658 
659         // Always pilfer pointers that are within sensor area or when alternate bouncer is showing
660         if (mActivePointerId != MotionEvent.INVALID_POINTER_ID
661                 || (mAlternateBouncerInteractor.isVisibleState()
662                 && !DeviceEntryUdfpsRefactor.isEnabled())) {
663             shouldPilfer = true;
664         }
665 
666         // Pilfer only once per gesture, don't pilfer for BP
667         if (shouldPilfer && !mPointerPilfered
668                 && getBiometricSessionType() != SESSION_BIOMETRIC_PROMPT) {
669             mInputManager.pilferPointers(
670                     mOverlay.getTouchOverlay().getViewRootImpl().getInputToken());
671             mPointerPilfered = true;
672         }
673 
674         return mActivePointerId != MotionEvent.INVALID_POINTER_ID;
675     }
676 
shouldTryToDismissKeyguard()677     private boolean shouldTryToDismissKeyguard() {
678         boolean onKeyguard = false;
679         if (DeviceEntryUdfpsRefactor.isEnabled()) {
680             onKeyguard = mKeyguardStateController.isShowing();
681         } else {
682             onKeyguard = mOverlay != null
683                     && mOverlay.getAnimationViewController()
684                         instanceof UdfpsKeyguardViewControllerLegacy;
685         }
686         return onKeyguard
687                 && mKeyguardStateController.canDismissLockScreen()
688                 && !mAttemptedToDismissKeyguard;
689     }
690 
691     @Inject
UdfpsController(@onNull Context context, @NonNull Execution execution, @NonNull LayoutInflater inflater, @Nullable FingerprintManager fingerprintManager, @NonNull WindowManager windowManager, @NonNull StatusBarStateController statusBarStateController, @Main DelayableExecutor fgExecutor, @NonNull StatusBarKeyguardViewManager statusBarKeyguardViewManager, @NonNull DumpManager dumpManager, @NonNull KeyguardUpdateMonitor keyguardUpdateMonitor, @NonNull FalsingManager falsingManager, @NonNull PowerManager powerManager, @NonNull AccessibilityManager accessibilityManager, @NonNull LockscreenShadeTransitionController lockscreenShadeTransitionController, @NonNull ScreenLifecycle screenLifecycle, @NonNull VibratorHelper vibrator, @NonNull UdfpsHapticsSimulator udfpsHapticsSimulator, @NonNull UdfpsShell udfpsShell, @NonNull KeyguardStateController keyguardStateController, @NonNull DisplayManager displayManager, @Main Handler mainHandler, @NonNull ConfigurationController configurationController, @NonNull SystemClock systemClock, @NonNull UnlockedScreenOffAnimationController unlockedScreenOffAnimationController, @NonNull SystemUIDialogManager dialogManager, @NonNull LatencyTracker latencyTracker, @NonNull ActivityTransitionAnimator activityTransitionAnimator, @NonNull @BiometricsBackground Executor biometricsExecutor, @NonNull PrimaryBouncerInteractor primaryBouncerInteractor, @NonNull ShadeInteractor shadeInteractor, @NonNull SinglePointerTouchProcessor singlePointerTouchProcessor, @NonNull SessionTracker sessionTracker, @NonNull AlternateBouncerInteractor alternateBouncerInteractor, @NonNull InputManager inputManager, @NonNull DeviceEntryFaceAuthInteractor deviceEntryFaceAuthInteractor, @NonNull UdfpsKeyguardAccessibilityDelegate udfpsKeyguardAccessibilityDelegate, @NonNull SelectedUserInteractor selectedUserInteractor, @NonNull KeyguardTransitionInteractor keyguardTransitionInteractor, Lazy<DeviceEntryUdfpsTouchOverlayViewModel> deviceEntryUdfpsTouchOverlayViewModel, Lazy<DefaultUdfpsTouchOverlayViewModel> defaultUdfpsTouchOverlayViewModel, @NonNull UdfpsOverlayInteractor udfpsOverlayInteractor, @NonNull PowerInteractor powerInteractor, @Application CoroutineScope scope)692     public UdfpsController(@NonNull Context context,
693             @NonNull Execution execution,
694             @NonNull LayoutInflater inflater,
695             @Nullable FingerprintManager fingerprintManager,
696             @NonNull WindowManager windowManager,
697             @NonNull StatusBarStateController statusBarStateController,
698             @Main DelayableExecutor fgExecutor,
699             @NonNull StatusBarKeyguardViewManager statusBarKeyguardViewManager,
700             @NonNull DumpManager dumpManager,
701             @NonNull KeyguardUpdateMonitor keyguardUpdateMonitor,
702             @NonNull FalsingManager falsingManager,
703             @NonNull PowerManager powerManager,
704             @NonNull AccessibilityManager accessibilityManager,
705             @NonNull LockscreenShadeTransitionController lockscreenShadeTransitionController,
706             @NonNull ScreenLifecycle screenLifecycle,
707             @NonNull VibratorHelper vibrator,
708             @NonNull UdfpsHapticsSimulator udfpsHapticsSimulator,
709             @NonNull UdfpsShell udfpsShell,
710             @NonNull KeyguardStateController keyguardStateController,
711             @NonNull DisplayManager displayManager,
712             @Main Handler mainHandler,
713             @NonNull ConfigurationController configurationController,
714             @NonNull SystemClock systemClock,
715             @NonNull UnlockedScreenOffAnimationController unlockedScreenOffAnimationController,
716             @NonNull SystemUIDialogManager dialogManager,
717             @NonNull LatencyTracker latencyTracker,
718             @NonNull ActivityTransitionAnimator activityTransitionAnimator,
719             @NonNull @BiometricsBackground Executor biometricsExecutor,
720             @NonNull PrimaryBouncerInteractor primaryBouncerInteractor,
721             @NonNull ShadeInteractor shadeInteractor,
722             @NonNull SinglePointerTouchProcessor singlePointerTouchProcessor,
723             @NonNull SessionTracker sessionTracker,
724             @NonNull AlternateBouncerInteractor alternateBouncerInteractor,
725             @NonNull InputManager inputManager,
726             @NonNull DeviceEntryFaceAuthInteractor deviceEntryFaceAuthInteractor,
727             @NonNull UdfpsKeyguardAccessibilityDelegate udfpsKeyguardAccessibilityDelegate,
728             @NonNull SelectedUserInteractor selectedUserInteractor,
729             @NonNull KeyguardTransitionInteractor keyguardTransitionInteractor,
730             Lazy<DeviceEntryUdfpsTouchOverlayViewModel> deviceEntryUdfpsTouchOverlayViewModel,
731             Lazy<DefaultUdfpsTouchOverlayViewModel> defaultUdfpsTouchOverlayViewModel,
732             @NonNull UdfpsOverlayInteractor udfpsOverlayInteractor,
733             @NonNull PowerInteractor powerInteractor,
734             @Application CoroutineScope scope) {
735         mContext = context;
736         mExecution = execution;
737         mVibrator = vibrator;
738         mInflater = inflater;
739         mIgnoreRefreshRate = mContext.getResources()
740                     .getBoolean(R.bool.config_ignoreUdfpsVote);
741         // The fingerprint manager is queried for UDFPS before this class is constructed, so the
742         // fingerprint manager should never be null.
743         mFingerprintManager = checkNotNull(fingerprintManager);
744         mWindowManager = windowManager;
745         mFgExecutor = fgExecutor;
746         mStatusBarStateController = statusBarStateController;
747         mKeyguardStateController = keyguardStateController;
748         mKeyguardViewManager = statusBarKeyguardViewManager;
749         mDumpManager = dumpManager;
750         mDialogManager = dialogManager;
751         mKeyguardUpdateMonitor = keyguardUpdateMonitor;
752         mFalsingManager = falsingManager;
753         mPowerManager = powerManager;
754         mAccessibilityManager = accessibilityManager;
755         mLockscreenShadeTransitionController = lockscreenShadeTransitionController;
756         screenLifecycle.addObserver(mScreenObserver);
757         mScreenOn = screenLifecycle.getScreenState() == ScreenLifecycle.SCREEN_ON;
758         mConfigurationController = configurationController;
759         mSystemClock = systemClock;
760         mUnlockedScreenOffAnimationController = unlockedScreenOffAnimationController;
761         mLatencyTracker = latencyTracker;
762         mActivityTransitionAnimator = activityTransitionAnimator;
763         mSensorProps = new FingerprintSensorPropertiesInternal(
764                 -1 /* sensorId */,
765                 SensorProperties.STRENGTH_CONVENIENCE,
766                 0 /* maxEnrollmentsPerUser */,
767                 new ArrayList<>() /* componentInfo */,
768                 FingerprintSensorProperties.TYPE_UNKNOWN,
769                 false /* resetLockoutRequiresHardwareAuthToken */);
770 
771         mBiometricExecutor = biometricsExecutor;
772         mPrimaryBouncerInteractor = primaryBouncerInteractor;
773         mShadeInteractor = shadeInteractor;
774         mAlternateBouncerInteractor = alternateBouncerInteractor;
775         mUdfpsOverlayInteractor = udfpsOverlayInteractor;
776         mPowerInteractor = powerInteractor;
777         mScope = scope;
778         mInputManager = inputManager;
779         mUdfpsKeyguardAccessibilityDelegate = udfpsKeyguardAccessibilityDelegate;
780         mSelectedUserInteractor = selectedUserInteractor;
781         mKeyguardTransitionInteractor = keyguardTransitionInteractor;
782 
783         mTouchProcessor = singlePointerTouchProcessor;
784         mSessionTracker = sessionTracker;
785         mDeviceEntryUdfpsTouchOverlayViewModel = deviceEntryUdfpsTouchOverlayViewModel;
786         mDefaultUdfpsTouchOverlayViewModel = defaultUdfpsTouchOverlayViewModel;
787 
788         mDumpManager.registerDumpable(TAG, this);
789 
790         mOrientationListener = new BiometricDisplayListener(
791                 context,
792                 displayManager,
793                 mainHandler,
794                 BiometricDisplayListener.SensorType.UnderDisplayFingerprint.INSTANCE,
795                 () -> {
796                     if (mAuthControllerUpdateUdfpsLocation != null) {
797                         mAuthControllerUpdateUdfpsLocation.run();
798                     }
799                     return Unit.INSTANCE;
800                 });
801         mDeviceEntryFaceAuthInteractor = deviceEntryFaceAuthInteractor;
802 
803         final UdfpsOverlayController mUdfpsOverlayController = new UdfpsOverlayController();
804         mFingerprintManager.setUdfpsOverlayController(mUdfpsOverlayController);
805 
806         final IntentFilter filter = new IntentFilter();
807         filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
808         context.registerReceiver(mBroadcastReceiver, filter,
809                 Context.RECEIVER_EXPORTED_UNAUDITED);
810 
811         udfpsHapticsSimulator.setUdfpsController(this);
812         udfpsShell.setUdfpsOverlayController(mUdfpsOverlayController);
813     }
814 
815     /**
816      * If a11y touchExplorationEnabled, play haptic to signal UDFPS scanning started.
817      */
818     @VisibleForTesting
playStartHaptic()819     public void playStartHaptic() {
820         if (mAccessibilityManager.isTouchExplorationEnabled()) {
821             if (mOverlay != null && mOverlay.getTouchOverlay() != null) {
822                 mVibrator.performHapticFeedback(
823                         mOverlay.getTouchOverlay(),
824                         HapticFeedbackConstants.CONTEXT_CLICK
825                 );
826             } else {
827                 Log.e(TAG, "No haptics played. Could not obtain overlay view to perform"
828                         + "vibration. Either the controller overlay is null or has no view");
829             }
830         }
831     }
832 
833     @Override
dozeTimeTick()834     public void dozeTimeTick() {
835         if (mOverlay != null && mOverlay.getTouchOverlay() instanceof UdfpsView) {
836             DeviceEntryUdfpsRefactor.assertInLegacyMode();
837             final View view = mOverlay.getTouchOverlay();
838             if (view != null) {
839                 ((UdfpsView) view).dozeTimeTick();
840             }
841         }
842     }
843 
redrawOverlay()844     private void redrawOverlay() {
845         UdfpsControllerOverlay overlay = mOverlay;
846         if (overlay != null) {
847             hideUdfpsOverlay();
848             showUdfpsOverlay(overlay);
849         }
850     }
851 
showUdfpsOverlay(@onNull UdfpsControllerOverlay overlay)852     private void showUdfpsOverlay(@NonNull UdfpsControllerOverlay overlay) {
853         mExecution.assertIsMainThread();
854 
855         mOverlay = overlay;
856         final int requestReason = overlay.getRequestReason();
857         if (requestReason == REASON_AUTH_KEYGUARD
858                 && !mKeyguardUpdateMonitor.isFingerprintDetectionRunning()) {
859             Log.d(TAG, "Attempting to showUdfpsOverlay when fingerprint detection"
860                     + " isn't running on keyguard. Skip show.");
861             return;
862         }
863         if (overlay.show(this, mOverlayParams)) {
864             Log.v(TAG, "showUdfpsOverlay | adding window reason=" + requestReason);
865             mOnFingerDown = false;
866             mAttemptedToDismissKeyguard = false;
867             mOrientationListener.enable();
868             if (mFingerprintManager != null) {
869                 mFingerprintManager.onUdfpsUiEvent(FingerprintManager.UDFPS_UI_OVERLAY_SHOWN,
870                         overlay.getRequestId(), mSensorProps.sensorId);
871             }
872         } else {
873             Log.v(TAG, "showUdfpsOverlay | the overlay is already showing");
874         }
875     }
876 
hideUdfpsOverlay()877     private void hideUdfpsOverlay() {
878         mExecution.assertIsMainThread();
879 
880         if (mOverlay != null) {
881             // Reset the controller back to its starting state.
882             final View oldView = mOverlay.getTouchOverlay();
883             if (oldView != null) {
884                 onFingerUp(mOverlay.getRequestId(), oldView);
885             }
886             final boolean removed = mOverlay.hide();
887             mKeyguardViewManager.hideAlternateBouncer(true);
888             Log.v(TAG, "hideUdfpsOverlay | removing window: " + removed);
889         } else {
890             Log.v(TAG, "hideUdfpsOverlay | the overlay is already hidden");
891         }
892 
893         mOverlay = null;
894         mOrientationListener.disable();
895     }
896 
unconfigureDisplay(View view)897     private void unconfigureDisplay(View view) {
898         if (!isOptical()) {
899             return;
900         }
901         if (DeviceEntryUdfpsRefactor.isEnabled()) {
902             if (mUdfpsDisplayMode != null) {
903                 mUdfpsDisplayMode.disable(null);
904             }
905         } else {
906             if (view != null) {
907                 UdfpsView udfpsView = (UdfpsView) view;
908                 if (udfpsView.isDisplayConfigured()) {
909                     udfpsView.unconfigureDisplay();
910                 }
911             }
912         }
913     }
914 
915     /**
916      * Request fingerprint scan.
917      *
918      * This is intended to be called in response to a sensor that triggers an AOD interrupt for the
919      * fingerprint sensor.
920      */
onAodInterrupt(int screenX, int screenY, float major, float minor)921     void onAodInterrupt(int screenX, int screenY, float major, float minor) {
922         if (mIsAodInterruptActive) {
923             return;
924         }
925 
926         if (!mKeyguardUpdateMonitor.isFingerprintDetectionRunning()) {
927             if (mFalsingManager.isFalseLongTap(FalsingManager.LOW_PENALTY)) {
928                 Log.v(TAG, "aod lock icon long-press rejected by the falsing manager.");
929                 return;
930             }
931             mKeyguardViewManager.showPrimaryBouncer(true);
932 
933             // play the same haptic as the DeviceEntryIcon longpress
934             if (mOverlay != null && mOverlay.getTouchOverlay() != null) {
935                 mVibrator.performHapticFeedback(
936                         mOverlay.getTouchOverlay(),
937                         UdfpsController.LONG_PRESS
938                 );
939             } else {
940                 Log.e(TAG, "No haptics played. Could not obtain overlay view to perform"
941                         + "vibration. Either the controller overlay is null or has no view");
942             }
943             return;
944         }
945 
946         // TODO(b/225068271): this may not be correct but there isn't a way to track it
947         final long requestId = mOverlay != null ? mOverlay.getRequestId() : -1;
948         mAodInterruptRunnable = () -> {
949             mIsAodInterruptActive = true;
950             // Since the sensor that triggers the AOD interrupt doesn't provide
951             // ACTION_UP/ACTION_CANCEL,  we need to be careful about not letting the screen
952             // accidentally remain in high brightness mode. As a mitigation, queue a call to
953             // cancel the fingerprint scan.
954             mCancelAodFingerUpAction = mFgExecutor.executeDelayed(this::tryAodSendFingerUp,
955                     AOD_SEND_FINGER_UP_DELAY_MILLIS);
956             // using a hard-coded value for orientation, time and gestureStart until they are
957             // available from the sensor.
958             onFingerDown(
959                     requestId,
960                     MotionEvent.INVALID_POINTER_ID /* pointerId */,
961                     screenX,
962                     screenY,
963                     minor,
964                     major,
965                     0f /* orientation */,
966                     0L /* time */,
967                     0L /* gestureStart */,
968                     true /* isAod */);
969         };
970 
971         if (mScreenOn) {
972             mAodInterruptRunnable.run();
973             mAodInterruptRunnable = null;
974         }
975     }
976 
977     /**
978      * Add a callback for fingerUp and fingerDown events
979      */
addCallback(Callback cb)980     public void addCallback(Callback cb) {
981         mCallbacks.add(cb);
982     }
983 
984     /**
985      * Remove callback
986      */
removeCallback(Callback cb)987     public void removeCallback(Callback cb) {
988         mCallbacks.remove(cb);
989     }
990 
991     /**
992      * The sensor that triggers {@link #onAodInterrupt} doesn't emit ACTION_UP or ACTION_CANCEL
993      * events, which means the fingerprint gesture created by the AOD interrupt needs to be
994      * cancelled manually.
995      * This should be called when authentication either succeeds or fails. Failing to cancel the
996      * scan will leave the display in the UDFPS mode until the user lifts their finger. On optical
997      * sensors, this can result in illumination persisting for longer than necessary.
998      */
999     @VisibleForTesting
tryAodSendFingerUp()1000     void tryAodSendFingerUp() {
1001         if (!mIsAodInterruptActive) {
1002             return;
1003         }
1004         cancelAodSendFingerUpAction();
1005         if (mOverlay != null && mOverlay.getTouchOverlay() != null) {
1006             onFingerUp(mOverlay.getRequestId(), mOverlay.getTouchOverlay());
1007         }
1008     }
1009 
1010     /**
1011      * Cancels any scheduled AoD finger-up actions without triggered the finger-up action. Only
1012      * call this method if the finger-up event has been guaranteed to have already occurred.
1013      */
1014     @VisibleForTesting
cancelAodSendFingerUpAction()1015     void cancelAodSendFingerUpAction() {
1016         mIsAodInterruptActive = false;
1017         if (mCancelAodFingerUpAction != null) {
1018             mCancelAodFingerUpAction.run();
1019             mCancelAodFingerUpAction = null;
1020         }
1021     }
1022 
isOptical()1023     private boolean isOptical() {
1024         return mSensorProps.sensorType == FingerprintSensorProperties.TYPE_UDFPS_OPTICAL;
1025     }
1026 
isFingerDown()1027     public boolean isFingerDown() {
1028         return mOnFingerDown;
1029     }
1030 
dispatchOnUiReady(long requestId)1031     private void dispatchOnUiReady(long requestId) {
1032         mFingerprintManager.onUdfpsUiEvent(FingerprintManager.UDFPS_UI_READY, requestId,
1033                 mSensorProps.sensorId);
1034         mLatencyTracker.onActionEnd(LatencyTracker.ACTION_UDFPS_ILLUMINATE);
1035     }
1036 
onFingerDown( long requestId, int x, int y, float minor, float major)1037     private void onFingerDown(
1038             long requestId,
1039             int x,
1040             int y,
1041             float minor,
1042             float major) {
1043         onFingerDown(
1044                 requestId,
1045                 MotionEvent.INVALID_POINTER_ID /* pointerId */,
1046                 x,
1047                 y,
1048                 minor,
1049                 major,
1050                 0f /* orientation */,
1051                 0L /* time */,
1052                 0L /* gestureStart */,
1053                 false /* isAod */);
1054     }
1055 
onFingerDown( long requestId, int pointerId, float x, float y, float minor, float major, float orientation, long time, long gestureStart, boolean isAod)1056     private void onFingerDown(
1057             long requestId,
1058             int pointerId,
1059             float x,
1060             float y,
1061             float minor,
1062             float major,
1063             float orientation,
1064             long time,
1065             long gestureStart,
1066             boolean isAod) {
1067         mExecution.assertIsMainThread();
1068 
1069         if (mOverlay == null) {
1070             Log.w(TAG, "Null request in onFingerDown");
1071             return;
1072         }
1073         if (!mOverlay.matchesRequestId(requestId)) {
1074             Log.w(TAG, "Mismatched fingerDown: " + requestId
1075                     + " current: " + mOverlay.getRequestId());
1076             return;
1077         }
1078         if (isOptical()) {
1079             mLatencyTracker.onActionStart(ACTION_UDFPS_ILLUMINATE);
1080         }
1081         // Refresh screen timeout and boost process priority if possible.
1082         mPowerManager.userActivity(mSystemClock.uptimeMillis(),
1083                 PowerManager.USER_ACTIVITY_EVENT_TOUCH, 0);
1084 
1085         if (!mOnFingerDown) {
1086             playStartHaptic();
1087 
1088             mDeviceEntryFaceAuthInteractor.onUdfpsSensorTouched();
1089         }
1090         mOnFingerDown = true;
1091         mFingerprintManager.onPointerDown(requestId, mSensorProps.sensorId, pointerId, x, y,
1092                 minor, major, orientation, time, gestureStart, isAod);
1093         Trace.endAsyncSection("UdfpsController.e2e.onPointerDown", 0);
1094 
1095         final View view = mOverlay.getTouchOverlay();
1096         if (view != null && isOptical()) {
1097             if (mIgnoreRefreshRate) {
1098                 dispatchOnUiReady(requestId);
1099             } else {
1100                 if (DeviceEntryUdfpsRefactor.isEnabled()) {
1101                     mUdfpsDisplayMode.enable(() -> dispatchOnUiReady(requestId));
1102                 } else {
1103                     ((UdfpsView) view).configureDisplay(() -> dispatchOnUiReady(requestId));
1104                 }
1105             }
1106         }
1107 
1108         for (Callback cb : mCallbacks) {
1109             cb.onFingerDown();
1110         }
1111     }
1112 
onFingerUp(long requestId, @NonNull View view)1113     private void onFingerUp(long requestId, @NonNull View view) {
1114         onFingerUp(
1115                 requestId,
1116                 view,
1117                 MotionEvent.INVALID_POINTER_ID /* pointerId */,
1118                 0f /* x */,
1119                 0f /* y */,
1120                 0f /* minor */,
1121                 0f /* major */,
1122                 0f /* orientation */,
1123                 0L /* time */,
1124                 0L /* gestureStart */,
1125                 false /* isAod */);
1126     }
1127 
onFingerUp( long requestId, View view, int pointerId, float x, float y, float minor, float major, float orientation, long time, long gestureStart, boolean isAod)1128     private void onFingerUp(
1129             long requestId,
1130             View view,
1131             int pointerId,
1132             float x,
1133             float y,
1134             float minor,
1135             float major,
1136             float orientation,
1137             long time,
1138             long gestureStart,
1139             boolean isAod) {
1140         mExecution.assertIsMainThread();
1141         mActivePointerId = MotionEvent.INVALID_POINTER_ID;
1142         mAcquiredReceived = false;
1143         if (mOnFingerDown) {
1144             mFingerprintManager.onPointerUp(requestId, mSensorProps.sensorId, pointerId, x,
1145                     y, minor, major, orientation, time, gestureStart, isAod);
1146             for (Callback cb : mCallbacks) {
1147                 cb.onFingerUp();
1148             }
1149         }
1150         mOnFingerDown = false;
1151         unconfigureDisplay(view);
1152         cancelAodSendFingerUpAction();
1153     }
1154 
1155     /**
1156      * Callback for fingerUp and fingerDown events.
1157      */
1158     public interface Callback {
1159         /**
1160          * Called onFingerUp events. Will only be called if the finger was previously down.
1161          */
onFingerUp()1162         void onFingerUp();
1163 
1164         /**
1165          * Called onFingerDown events.
1166          */
onFingerDown()1167         void onFingerDown();
1168     }
1169 }
1170