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