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         // Wait to receive up or cancel before pausing auth
557         if (mActivePointerId == MotionEvent.INVALID_POINTER_ID
558                 && mOverlay.getAnimationViewController().shouldPauseAuth()) {
559             Log.w(TAG, "ignoring onTouch with shouldPauseAuth = true");
560             return false;
561         }
562         if (!mOverlay.matchesRequestId(requestId)) {
563             Log.w(TAG, "ignoring stale touch event: " + requestId + " current: "
564                     + mOverlay.getRequestId());
565             return false;
566         }
567         if (event.getAction() == MotionEvent.ACTION_DOWN
568                 || event.getAction() == MotionEvent.ACTION_HOVER_ENTER) {
569             // Reset on ACTION_DOWN, start of new gesture
570             mPointerPilfered = false;
571         }
572 
573         final TouchProcessorResult result = mTouchProcessor.processTouch(event, mActivePointerId,
574                 mOverlayParams);
575         if (result instanceof TouchProcessorResult.Failure) {
576             Log.w(TAG, ((TouchProcessorResult.Failure) result).getReason());
577             return false;
578         }
579 
580         final TouchProcessorResult.ProcessedTouch processedTouch =
581                 (TouchProcessorResult.ProcessedTouch) result;
582         final NormalizedTouchData data = processedTouch.getTouchData();
583 
584         boolean shouldPilfer = false;
585         mActivePointerId = processedTouch.getPointerOnSensorId();
586         switch (processedTouch.getEvent()) {
587             case DOWN:
588                 if (shouldTryToDismissKeyguard()) {
589                     tryDismissingKeyguard();
590                 }
591                 if (!mOnFingerDown) {
592                     onFingerDown(requestId,
593                             data.getPointerId(),
594                             data.getX(),
595                             data.getY(),
596                             data.getMinor(),
597                             data.getMajor(),
598                             data.getOrientation(),
599                             data.getTime(),
600                             data.getGestureStart(),
601                             mStatusBarStateController.isDozing());
602                 }
603 
604                 // Pilfer if valid overlap, don't allow following events to reach keyguard
605                 shouldPilfer = true;
606 
607                 // Touch is a valid UDFPS touch. Inform the falsing manager so that the touch
608                 // isn't counted against the falsing algorithm as an accidental touch.
609                 // We do this on the DOWN event instead of CANCEL/UP because the CANCEL/UP events
610                 // get sent too late to this receiver (after the actual cancel/up motions occur),
611                 // and therefore wouldn't end up being used as part of the falsing algo.
612                 mFalsingManager.isFalseTouch(UDFPS_AUTHENTICATION);
613                 break;
614 
615             case UP:
616             case CANCEL:
617                 if (InteractionEvent.CANCEL.equals(processedTouch.getEvent())) {
618                     Log.w(TAG, "This is a CANCEL event that's reported as an UP event!");
619                 }
620                 mAttemptedToDismissKeyguard = false;
621                 onFingerUp(requestId,
622                         mOverlay.getOverlayView(),
623                         data.getPointerId(),
624                         data.getX(),
625                         data.getY(),
626                         data.getMinor(),
627                         data.getMajor(),
628                         data.getOrientation(),
629                         data.getTime(),
630                         data.getGestureStart(),
631                         mStatusBarStateController.isDozing());
632                 break;
633 
634             case UNCHANGED:
635                 if (!isWithinSensorArea(mOverlay.getOverlayView(), event.getRawX(), event.getRawY(),
636                         true) && mActivePointerId == MotionEvent.INVALID_POINTER_ID
637                         && mAlternateBouncerInteractor.isVisibleState()) {
638                     // No pointer on sensor, forward to keyguard if alternateBouncer is visible
639                     mKeyguardViewManager.onTouch(event);
640                 }
641 
642             default:
643                 break;
644         }
645         logBiometricTouch(processedTouch.getEvent(), data);
646 
647         // Always pilfer pointers that are within sensor area or when alternate bouncer is showing
648         if (isWithinSensorArea(mOverlay.getOverlayView(), event.getRawX(), event.getRawY(), true)
649                 || mAlternateBouncerInteractor.isVisibleState()) {
650             shouldPilfer = true;
651         }
652 
653         // Pilfer only once per gesture, don't pilfer for BP
654         if (shouldPilfer && !mPointerPilfered
655                 && getBiometricSessionType() != SESSION_BIOMETRIC_PROMPT) {
656             mInputManager.pilferPointers(
657                     mOverlay.getOverlayView().getViewRootImpl().getInputToken());
658             mPointerPilfered = true;
659         }
660 
661         return processedTouch.getTouchData().isWithinBounds(mOverlayParams.getNativeSensorBounds());
662     }
663 
oldOnTouch(long requestId, @NonNull MotionEvent event, boolean fromUdfpsView)664     private boolean oldOnTouch(long requestId, @NonNull MotionEvent event, boolean fromUdfpsView) {
665         if (mOverlay == null) {
666             Log.w(TAG, "ignoring onTouch with null overlay");
667             return false;
668         }
669         if (!mOverlay.matchesRequestId(requestId)) {
670             Log.w(TAG, "ignoring stale touch event: " + requestId + " current: "
671                     + mOverlay.getRequestId());
672             return false;
673         }
674 
675         final UdfpsView udfpsView = mOverlay.getOverlayView();
676         boolean handled = false;
677         switch (event.getActionMasked()) {
678             case MotionEvent.ACTION_DOWN:
679             case MotionEvent.ACTION_HOVER_ENTER:
680                 Trace.beginSection("UdfpsController.onTouch.ACTION_DOWN");
681                 // To simplify the lifecycle of the velocity tracker, make sure it's never null
682                 // after ACTION_DOWN, and always null after ACTION_CANCEL or ACTION_UP.
683                 if (mVelocityTracker == null) {
684                     mVelocityTracker = VelocityTracker.obtain();
685                 } else {
686                     // ACTION_UP or ACTION_CANCEL is not guaranteed to be called before a new
687                     // ACTION_DOWN, in that case we should just reuse the old instance.
688                     mVelocityTracker.clear();
689                 }
690 
691                 final boolean withinSensorArea =
692                         isWithinSensorArea(udfpsView, event.getX(), event.getY(), fromUdfpsView);
693                 if (withinSensorArea) {
694                     Trace.beginAsyncSection("UdfpsController.e2e.onPointerDown", 0);
695                     Log.v(TAG, "onTouch | action down");
696                     // The pointer that causes ACTION_DOWN is always at index 0.
697                     // We need to persist its ID to track it during ACTION_MOVE that could include
698                     // data for many other pointers because of multi-touch support.
699                     mActivePointerId = event.getPointerId(0);
700                     mVelocityTracker.addMovement(event);
701                     handled = true;
702                     mAcquiredReceived = false;
703                 }
704                 if ((withinSensorArea || fromUdfpsView) && shouldTryToDismissKeyguard()) {
705                     Log.v(TAG, "onTouch | dismiss keyguard ACTION_DOWN");
706                     tryDismissingKeyguard();
707                 }
708 
709                 Trace.endSection();
710                 break;
711 
712             case MotionEvent.ACTION_MOVE:
713             case MotionEvent.ACTION_HOVER_MOVE:
714                 Trace.beginSection("UdfpsController.onTouch.ACTION_MOVE");
715                 final int idx = mActivePointerId == -1
716                         ? event.getPointerId(0)
717                         : event.findPointerIndex(mActivePointerId);
718                 if (idx == event.getActionIndex()) {
719                     final boolean actionMoveWithinSensorArea =
720                             isWithinSensorArea(udfpsView, event.getX(idx), event.getY(idx),
721                                     fromUdfpsView);
722                     if ((fromUdfpsView || actionMoveWithinSensorArea)
723                             && shouldTryToDismissKeyguard()) {
724                         Log.v(TAG, "onTouch | dismiss keyguard ACTION_MOVE");
725                         tryDismissingKeyguard();
726                         break;
727                     }
728                     // Map the touch to portrait mode if the device is in landscape mode.
729                     final Point scaledTouch = mUdfpsUtils.getTouchInNativeCoordinates(
730                             idx, event, mOverlayParams);
731                     if (actionMoveWithinSensorArea) {
732                         if (mVelocityTracker == null) {
733                             // touches could be injected, so the velocity tracker may not have
734                             // been initialized (via ACTION_DOWN).
735                             mVelocityTracker = VelocityTracker.obtain();
736                         }
737                         mVelocityTracker.addMovement(event);
738                         // Compute pointer velocity in pixels per second.
739                         mVelocityTracker.computeCurrentVelocity(1000);
740                         // Compute pointer speed from X and Y velocities.
741                         final float v = computePointerSpeed(mVelocityTracker, mActivePointerId);
742                         final float minor = event.getTouchMinor(idx);
743                         final float major = event.getTouchMajor(idx);
744                         final boolean exceedsVelocityThreshold = exceedsVelocityThreshold(v);
745                         final String touchInfo = String.format(
746                                 "minor: %.1f, major: %.1f, v: %.1f, exceedsVelocityThreshold: %b",
747                                 minor, major, v, exceedsVelocityThreshold);
748                         final long sinceLastLog = mSystemClock.elapsedRealtime() - mTouchLogTime;
749 
750                         if (!mOnFingerDown && !mAcquiredReceived && !exceedsVelocityThreshold) {
751                             final float scale = mOverlayParams.getScaleFactor();
752                             float scaledMinor = minor / scale;
753                             float scaledMajor = major / scale;
754                             onFingerDown(requestId, scaledTouch.x, scaledTouch.y, scaledMinor,
755                                     scaledMajor);
756 
757                             Log.v(TAG, "onTouch | finger down: " + touchInfo);
758                             mTouchLogTime = mSystemClock.elapsedRealtime();
759                             handled = true;
760                         } else if (sinceLastLog >= MIN_TOUCH_LOG_INTERVAL) {
761                             Log.v(TAG, "onTouch | finger move: " + touchInfo);
762                             mTouchLogTime = mSystemClock.elapsedRealtime();
763                         }
764                     } else {
765                         Log.v(TAG, "onTouch | finger outside");
766                         onFingerUp(requestId, udfpsView);
767                         // Maybe announce for accessibility.
768                         mFgExecutor.execute(() -> {
769                             if (mOverlay == null) {
770                                 Log.e(TAG, "touch outside sensor area received"
771                                         + "but serverRequest is null");
772                                 return;
773                             }
774                             mOverlay.onTouchOutsideOfSensorArea(scaledTouch);
775                         });
776                     }
777                 }
778                 Trace.endSection();
779                 break;
780 
781             case MotionEvent.ACTION_UP:
782             case MotionEvent.ACTION_CANCEL:
783             case MotionEvent.ACTION_HOVER_EXIT:
784                 Trace.beginSection("UdfpsController.onTouch.ACTION_UP");
785                 mActivePointerId = -1;
786                 if (mVelocityTracker != null) {
787                     mVelocityTracker.recycle();
788                     mVelocityTracker = null;
789                 }
790                 Log.v(TAG, "onTouch | finger up");
791                 mAttemptedToDismissKeyguard = false;
792                 onFingerUp(requestId, udfpsView);
793                 mFalsingManager.isFalseTouch(UDFPS_AUTHENTICATION);
794                 Trace.endSection();
795                 break;
796 
797             default:
798                 // Do nothing.
799         }
800         return handled;
801     }
802 
shouldTryToDismissKeyguard()803     private boolean shouldTryToDismissKeyguard() {
804         return mOverlay != null
805                 && mOverlay.getAnimationViewController()
806                 instanceof UdfpsKeyguardViewControllerAdapter
807                 && mKeyguardStateController.canDismissLockScreen()
808                 && !mAttemptedToDismissKeyguard;
809     }
810 
811     @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)812     public UdfpsController(@NonNull Context context,
813             @NonNull Execution execution,
814             @NonNull LayoutInflater inflater,
815             @Nullable FingerprintManager fingerprintManager,
816             @NonNull WindowManager windowManager,
817             @NonNull StatusBarStateController statusBarStateController,
818             @Main DelayableExecutor fgExecutor,
819             @NonNull ShadeExpansionStateManager shadeExpansionStateManager,
820             @NonNull StatusBarKeyguardViewManager statusBarKeyguardViewManager,
821             @NonNull DumpManager dumpManager,
822             @NonNull KeyguardUpdateMonitor keyguardUpdateMonitor,
823             @NonNull FeatureFlags featureFlags,
824             @NonNull FalsingManager falsingManager,
825             @NonNull PowerManager powerManager,
826             @NonNull AccessibilityManager accessibilityManager,
827             @NonNull LockscreenShadeTransitionController lockscreenShadeTransitionController,
828             @NonNull ScreenLifecycle screenLifecycle,
829             @NonNull VibratorHelper vibrator,
830             @NonNull UdfpsHapticsSimulator udfpsHapticsSimulator,
831             @NonNull UdfpsShell udfpsShell,
832             @NonNull KeyguardStateController keyguardStateController,
833             @NonNull DisplayManager displayManager,
834             @Main Handler mainHandler,
835             @NonNull ConfigurationController configurationController,
836             @NonNull SystemClock systemClock,
837             @NonNull UnlockedScreenOffAnimationController unlockedScreenOffAnimationController,
838             @NonNull SystemUIDialogManager dialogManager,
839             @NonNull LatencyTracker latencyTracker,
840             @NonNull ActivityLaunchAnimator activityLaunchAnimator,
841             @NonNull Optional<Provider<AlternateUdfpsTouchProvider>> alternateTouchProvider,
842             @NonNull @BiometricsBackground Executor biometricsExecutor,
843             @NonNull PrimaryBouncerInteractor primaryBouncerInteractor,
844             @NonNull SinglePointerTouchProcessor singlePointerTouchProcessor,
845             @NonNull SessionTracker sessionTracker,
846             @NonNull AlternateBouncerInteractor alternateBouncerInteractor,
847             @NonNull SecureSettings secureSettings,
848             @NonNull InputManager inputManager,
849             @NonNull UdfpsUtils udfpsUtils,
850             @NonNull KeyguardFaceAuthInteractor keyguardFaceAuthInteractor,
851             @NonNull UdfpsKeyguardAccessibilityDelegate udfpsKeyguardAccessibilityDelegate,
852             @NonNull Provider<UdfpsKeyguardViewModels> udfpsKeyguardViewModelsProvider) {
853         mContext = context;
854         mExecution = execution;
855         mVibrator = vibrator;
856         mInflater = inflater;
857         mIgnoreRefreshRate = mContext.getResources()
858                     .getBoolean(R.bool.config_ignoreUdfpsVote);
859         // The fingerprint manager is queried for UDFPS before this class is constructed, so the
860         // fingerprint manager should never be null.
861         mFingerprintManager = checkNotNull(fingerprintManager);
862         mWindowManager = windowManager;
863         mFgExecutor = fgExecutor;
864         mShadeExpansionStateManager = shadeExpansionStateManager;
865         mStatusBarStateController = statusBarStateController;
866         mKeyguardStateController = keyguardStateController;
867         mKeyguardViewManager = statusBarKeyguardViewManager;
868         mDumpManager = dumpManager;
869         mDialogManager = dialogManager;
870         mKeyguardUpdateMonitor = keyguardUpdateMonitor;
871         mFeatureFlags = featureFlags;
872         mFalsingManager = falsingManager;
873         mPowerManager = powerManager;
874         mAccessibilityManager = accessibilityManager;
875         mLockscreenShadeTransitionController = lockscreenShadeTransitionController;
876         screenLifecycle.addObserver(mScreenObserver);
877         mScreenOn = screenLifecycle.getScreenState() == ScreenLifecycle.SCREEN_ON;
878         mConfigurationController = configurationController;
879         mSystemClock = systemClock;
880         mUnlockedScreenOffAnimationController = unlockedScreenOffAnimationController;
881         mLatencyTracker = latencyTracker;
882         mActivityLaunchAnimator = activityLaunchAnimator;
883         mAlternateTouchProvider = alternateTouchProvider.map(Provider::get).orElse(null);
884         mSensorProps = new FingerprintSensorPropertiesInternal(
885                 -1 /* sensorId */,
886                 SensorProperties.STRENGTH_CONVENIENCE,
887                 0 /* maxEnrollmentsPerUser */,
888                 new ArrayList<>() /* componentInfo */,
889                 FingerprintSensorProperties.TYPE_UNKNOWN,
890                 false /* resetLockoutRequiresHardwareAuthToken */);
891 
892         mBiometricExecutor = biometricsExecutor;
893         mPrimaryBouncerInteractor = primaryBouncerInteractor;
894         mAlternateBouncerInteractor = alternateBouncerInteractor;
895         mSecureSettings = secureSettings;
896         mUdfpsUtils = udfpsUtils;
897         mInputManager = inputManager;
898         mUdfpsKeyguardAccessibilityDelegate = udfpsKeyguardAccessibilityDelegate;
899 
900         mTouchProcessor = mFeatureFlags.isEnabled(Flags.UDFPS_NEW_TOUCH_DETECTION)
901                 ? singlePointerTouchProcessor : null;
902         mSessionTracker = sessionTracker;
903 
904         mDumpManager.registerDumpable(TAG, this);
905 
906         mOrientationListener = new BiometricDisplayListener(
907                 context,
908                 displayManager,
909                 mainHandler,
910                 BiometricDisplayListener.SensorType.UnderDisplayFingerprint.INSTANCE,
911                 () -> {
912                     if (mAuthControllerUpdateUdfpsLocation != null) {
913                         mAuthControllerUpdateUdfpsLocation.run();
914                     }
915                     return Unit.INSTANCE;
916                 });
917         mKeyguardFaceAuthInteractor = keyguardFaceAuthInteractor;
918         mUdfpsKeyguardViewModels = udfpsKeyguardViewModelsProvider;
919 
920         final UdfpsOverlayController mUdfpsOverlayController = new UdfpsOverlayController();
921         mFingerprintManager.setUdfpsOverlayController(mUdfpsOverlayController);
922 
923         final IntentFilter filter = new IntentFilter();
924         filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
925         context.registerReceiver(mBroadcastReceiver, filter,
926                 Context.RECEIVER_EXPORTED_UNAUDITED);
927 
928         udfpsHapticsSimulator.setUdfpsController(this);
929         udfpsShell.setUdfpsOverlayController(mUdfpsOverlayController);
930     }
931 
932     /**
933      * If a11y touchExplorationEnabled, play haptic to signal UDFPS scanning started.
934      */
935     @VisibleForTesting
playStartHaptic()936     public void playStartHaptic() {
937         if (mAccessibilityManager.isTouchExplorationEnabled()) {
938             if (mFeatureFlags.isEnabled(ONE_WAY_HAPTICS_API_MIGRATION)) {
939                 if (mOverlay != null && mOverlay.getOverlayView() != null) {
940                     mVibrator.performHapticFeedback(
941                             mOverlay.getOverlayView(),
942                             HapticFeedbackConstants.CONTEXT_CLICK
943                     );
944                 } else {
945                     Log.e(TAG, "No haptics played. Could not obtain overlay view to perform"
946                             + "vibration. Either the controller overlay is null or has no view");
947                 }
948             } else {
949                 mVibrator.vibrate(
950                         Process.myUid(),
951                         mContext.getOpPackageName(),
952                         EFFECT_CLICK,
953                         "udfps-onStart-click",
954                         UDFPS_VIBRATION_ATTRIBUTES);
955             }
956         }
957     }
958 
959     @Override
dozeTimeTick()960     public void dozeTimeTick() {
961         if (mOverlay != null) {
962             final UdfpsView view = mOverlay.getOverlayView();
963             if (view != null) {
964                 view.dozeTimeTick();
965             }
966         }
967     }
968 
redrawOverlay()969     private void redrawOverlay() {
970         UdfpsControllerOverlay overlay = mOverlay;
971         if (overlay != null) {
972             hideUdfpsOverlay();
973             showUdfpsOverlay(overlay);
974         }
975     }
976 
showUdfpsOverlay(@onNull UdfpsControllerOverlay overlay)977     private void showUdfpsOverlay(@NonNull UdfpsControllerOverlay overlay) {
978         mExecution.assertIsMainThread();
979 
980         mOverlay = overlay;
981         final int requestReason = overlay.getRequestReason();
982         if (requestReason == REASON_AUTH_KEYGUARD
983                 && !mKeyguardUpdateMonitor.isFingerprintDetectionRunning()) {
984             Log.d(TAG, "Attempting to showUdfpsOverlay when fingerprint detection"
985                     + " isn't running on keyguard. Skip show.");
986             return;
987         }
988         if (overlay.show(this, mOverlayParams)) {
989             Log.v(TAG, "showUdfpsOverlay | adding window reason=" + requestReason);
990             mOnFingerDown = false;
991             mAttemptedToDismissKeyguard = false;
992             mOrientationListener.enable();
993             if (mFingerprintManager != null) {
994                 mFingerprintManager.onUdfpsUiEvent(FingerprintManager.UDFPS_UI_OVERLAY_SHOWN,
995                         overlay.getRequestId(), mSensorProps.sensorId);
996             }
997         } else {
998             Log.v(TAG, "showUdfpsOverlay | the overlay is already showing");
999         }
1000     }
1001 
hideUdfpsOverlay()1002     private void hideUdfpsOverlay() {
1003         mExecution.assertIsMainThread();
1004 
1005         if (mOverlay != null) {
1006             // Reset the controller back to its starting state.
1007             final UdfpsView oldView = mOverlay.getOverlayView();
1008             if (oldView != null) {
1009                 onFingerUp(mOverlay.getRequestId(), oldView);
1010             }
1011             final boolean removed = mOverlay.hide();
1012             mKeyguardViewManager.hideAlternateBouncer(true);
1013             Log.v(TAG, "hideUdfpsOverlay | removing window: " + removed);
1014         } else {
1015             Log.v(TAG, "hideUdfpsOverlay | the overlay is already hidden");
1016         }
1017 
1018         mOverlay = null;
1019         mOrientationListener.disable();
1020 
1021     }
1022 
unconfigureDisplay(@onNull UdfpsView view)1023     private void unconfigureDisplay(@NonNull UdfpsView view) {
1024         if (view.isDisplayConfigured()) {
1025             view.unconfigureDisplay();
1026         }
1027     }
1028 
1029     /**
1030      * Request fingerprint scan.
1031      *
1032      * This is intended to be called in response to a sensor that triggers an AOD interrupt for the
1033      * fingerprint sensor.
1034      */
onAodInterrupt(int screenX, int screenY, float major, float minor)1035     void onAodInterrupt(int screenX, int screenY, float major, float minor) {
1036         if (mIsAodInterruptActive) {
1037             return;
1038         }
1039 
1040         if (!mKeyguardUpdateMonitor.isFingerprintDetectionRunning()) {
1041             if (mFalsingManager.isFalseLongTap(FalsingManager.LOW_PENALTY)) {
1042                 Log.v(TAG, "aod lock icon long-press rejected by the falsing manager.");
1043                 return;
1044             }
1045             mKeyguardViewManager.showPrimaryBouncer(true);
1046 
1047             // play the same haptic as the LockIconViewController longpress
1048             if (mFeatureFlags.isEnabled(ONE_WAY_HAPTICS_API_MIGRATION)) {
1049                 if (mOverlay != null && mOverlay.getOverlayView() != null) {
1050                     mVibrator.performHapticFeedback(
1051                             mOverlay.getOverlayView(),
1052                             UdfpsController.LONG_PRESS
1053                     );
1054                 } else {
1055                     Log.e(TAG, "No haptics played. Could not obtain overlay view to perform"
1056                             + "vibration. Either the controller overlay is null or has no view");
1057                 }
1058             } else {
1059                 mVibrator.vibrate(
1060                         Process.myUid(),
1061                         mContext.getOpPackageName(),
1062                         UdfpsController.EFFECT_CLICK,
1063                         "aod-lock-icon-longpress",
1064                         LOCK_ICON_VIBRATION_ATTRIBUTES);
1065             }
1066             return;
1067         }
1068 
1069         // TODO(b/225068271): this may not be correct but there isn't a way to track it
1070         final long requestId = mOverlay != null ? mOverlay.getRequestId() : -1;
1071         mAodInterruptRunnable = () -> {
1072             mIsAodInterruptActive = true;
1073             // Since the sensor that triggers the AOD interrupt doesn't provide
1074             // ACTION_UP/ACTION_CANCEL,  we need to be careful about not letting the screen
1075             // accidentally remain in high brightness mode. As a mitigation, queue a call to
1076             // cancel the fingerprint scan.
1077             mCancelAodFingerUpAction = mFgExecutor.executeDelayed(this::tryAodSendFingerUp,
1078                     AOD_SEND_FINGER_UP_DELAY_MILLIS);
1079             // using a hard-coded value for major and minor until it is available from the sensor
1080             onFingerDown(requestId, screenX, screenY, minor, major);
1081         };
1082 
1083         if (mScreenOn) {
1084             mAodInterruptRunnable.run();
1085             mAodInterruptRunnable = null;
1086         }
1087     }
1088 
1089     /**
1090      * Add a callback for fingerUp and fingerDown events
1091      */
addCallback(Callback cb)1092     public void addCallback(Callback cb) {
1093         mCallbacks.add(cb);
1094     }
1095 
1096     /**
1097      * Remove callback
1098      */
removeCallback(Callback cb)1099     public void removeCallback(Callback cb) {
1100         mCallbacks.remove(cb);
1101     }
1102 
1103     /**
1104      * The sensor that triggers {@link #onAodInterrupt} doesn't emit ACTION_UP or ACTION_CANCEL
1105      * events, which means the fingerprint gesture created by the AOD interrupt needs to be
1106      * cancelled manually.
1107      * This should be called when authentication either succeeds or fails. Failing to cancel the
1108      * scan will leave the display in the UDFPS mode until the user lifts their finger. On optical
1109      * sensors, this can result in illumination persisting for longer than necessary.
1110      */
1111     @VisibleForTesting
tryAodSendFingerUp()1112     void tryAodSendFingerUp() {
1113         if (!mIsAodInterruptActive) {
1114             return;
1115         }
1116         cancelAodSendFingerUpAction();
1117         if (mOverlay != null && mOverlay.getOverlayView() != null) {
1118             onFingerUp(mOverlay.getRequestId(), mOverlay.getOverlayView());
1119         }
1120     }
1121 
1122     /**
1123      * Cancels any scheduled AoD finger-up actions without triggered the finger-up action. Only
1124      * call this method if the finger-up event has been guaranteed to have already occurred.
1125      */
1126     @VisibleForTesting
cancelAodSendFingerUpAction()1127     void cancelAodSendFingerUpAction() {
1128         mIsAodInterruptActive = false;
1129         if (mCancelAodFingerUpAction != null) {
1130             mCancelAodFingerUpAction.run();
1131             mCancelAodFingerUpAction = null;
1132         }
1133     }
1134 
isOptical()1135     private boolean isOptical() {
1136         return mSensorProps.sensorType == FingerprintSensorProperties.TYPE_UDFPS_OPTICAL;
1137     }
1138 
isFingerDown()1139     public boolean isFingerDown() {
1140         return mOnFingerDown;
1141     }
1142 
dispatchOnUiReady(long requestId)1143     private void dispatchOnUiReady(long requestId) {
1144         if (mAlternateTouchProvider != null) {
1145             mBiometricExecutor.execute(() -> {
1146                 mAlternateTouchProvider.onUiReady();
1147                 mLatencyTracker.onActionEnd(LatencyTracker.ACTION_UDFPS_ILLUMINATE);
1148             });
1149         } else {
1150             mFingerprintManager.onUdfpsUiEvent(FingerprintManager.UDFPS_UI_READY, requestId,
1151                     mSensorProps.sensorId);
1152             mLatencyTracker.onActionEnd(LatencyTracker.ACTION_UDFPS_ILLUMINATE);
1153         }
1154     }
1155 
onFingerDown( long requestId, int x, int y, float minor, float major)1156     private void onFingerDown(
1157             long requestId,
1158             int x,
1159             int y,
1160             float minor,
1161             float major) {
1162         onFingerDown(
1163                 requestId,
1164                 MotionEvent.INVALID_POINTER_ID /* pointerId */,
1165                 x,
1166                 y,
1167                 minor,
1168                 major,
1169                 0f /* orientation */,
1170                 0L /* time */,
1171                 0L /* gestureStart */,
1172                 false /* isAod */);
1173     }
1174 
onFingerDown( long requestId, int pointerId, float x, float y, float minor, float major, float orientation, long time, long gestureStart, boolean isAod)1175     private void onFingerDown(
1176             long requestId,
1177             int pointerId,
1178             float x,
1179             float y,
1180             float minor,
1181             float major,
1182             float orientation,
1183             long time,
1184             long gestureStart,
1185             boolean isAod) {
1186         mExecution.assertIsMainThread();
1187 
1188         if (mOverlay == null) {
1189             Log.w(TAG, "Null request in onFingerDown");
1190             return;
1191         }
1192         if (!mOverlay.matchesRequestId(requestId)) {
1193             Log.w(TAG, "Mismatched fingerDown: " + requestId
1194                     + " current: " + mOverlay.getRequestId());
1195             return;
1196         }
1197         if (isOptical()) {
1198             mLatencyTracker.onActionStart(LatencyTracker.ACTION_UDFPS_ILLUMINATE);
1199         }
1200         // Refresh screen timeout and boost process priority if possible.
1201         mPowerManager.userActivity(mSystemClock.uptimeMillis(),
1202                 PowerManager.USER_ACTIVITY_EVENT_TOUCH, 0);
1203 
1204         if (!mOnFingerDown) {
1205             playStartHaptic();
1206 
1207             mKeyguardFaceAuthInteractor.onUdfpsSensorTouched();
1208             if (!mKeyguardUpdateMonitor.isFaceDetectionRunning()) {
1209                 mKeyguardUpdateMonitor.requestFaceAuth(FaceAuthApiRequestReason.UDFPS_POINTER_DOWN);
1210             }
1211         }
1212         mOnFingerDown = true;
1213         if (mAlternateTouchProvider != null) {
1214             mBiometricExecutor.execute(() -> {
1215                 mAlternateTouchProvider.onPointerDown(requestId, (int) x, (int) y, minor, major);
1216             });
1217             mFgExecutor.execute(() -> {
1218                 if (mKeyguardUpdateMonitor.isFingerprintDetectionRunning()) {
1219                     mKeyguardUpdateMonitor.onUdfpsPointerDown((int) requestId);
1220                 }
1221             });
1222         } else {
1223             if (mFeatureFlags.isEnabled(Flags.UDFPS_NEW_TOUCH_DETECTION)) {
1224                 mFingerprintManager.onPointerDown(requestId, mSensorProps.sensorId, pointerId, x, y,
1225                         minor, major, orientation, time, gestureStart, isAod);
1226             } else {
1227                 mFingerprintManager.onPointerDown(requestId, mSensorProps.sensorId, (int) x,
1228                         (int) y, minor, major);
1229             }
1230         }
1231         Trace.endAsyncSection("UdfpsController.e2e.onPointerDown", 0);
1232         final UdfpsView view = mOverlay.getOverlayView();
1233         if (view != null && isOptical()) {
1234             if (mIgnoreRefreshRate) {
1235                 dispatchOnUiReady(requestId);
1236             } else {
1237                 view.configureDisplay(() -> dispatchOnUiReady(requestId));
1238             }
1239         }
1240 
1241         for (Callback cb : mCallbacks) {
1242             cb.onFingerDown();
1243         }
1244     }
1245 
onFingerUp(long requestId, @NonNull UdfpsView view)1246     private void onFingerUp(long requestId, @NonNull UdfpsView view) {
1247         onFingerUp(
1248                 requestId,
1249                 view,
1250                 MotionEvent.INVALID_POINTER_ID /* pointerId */,
1251                 0f /* x */,
1252                 0f /* y */,
1253                 0f /* minor */,
1254                 0f /* major */,
1255                 0f /* orientation */,
1256                 0L /* time */,
1257                 0L /* gestureStart */,
1258                 false /* isAod */);
1259     }
1260 
onFingerUp( long requestId, @NonNull UdfpsView view, int pointerId, float x, float y, float minor, float major, float orientation, long time, long gestureStart, boolean isAod)1261     private void onFingerUp(
1262             long requestId,
1263             @NonNull UdfpsView view,
1264             int pointerId,
1265             float x,
1266             float y,
1267             float minor,
1268             float major,
1269             float orientation,
1270             long time,
1271             long gestureStart,
1272             boolean isAod) {
1273         mExecution.assertIsMainThread();
1274         mActivePointerId = -1;
1275         mAcquiredReceived = false;
1276         if (mOnFingerDown) {
1277             if (mAlternateTouchProvider != null) {
1278                 mBiometricExecutor.execute(() -> {
1279                     mAlternateTouchProvider.onPointerUp(requestId);
1280                 });
1281                 mFgExecutor.execute(() -> {
1282                     if (mKeyguardUpdateMonitor.isFingerprintDetectionRunning()) {
1283                         mKeyguardUpdateMonitor.onUdfpsPointerUp((int) requestId);
1284                     }
1285                 });
1286             } else {
1287                 if (mFeatureFlags.isEnabled(Flags.UDFPS_NEW_TOUCH_DETECTION)) {
1288                     mFingerprintManager.onPointerUp(requestId, mSensorProps.sensorId, pointerId, x,
1289                             y, minor, major, orientation, time, gestureStart, isAod);
1290                 } else {
1291                     mFingerprintManager.onPointerUp(requestId, mSensorProps.sensorId);
1292                 }
1293             }
1294             for (Callback cb : mCallbacks) {
1295                 cb.onFingerUp();
1296             }
1297         }
1298         mOnFingerDown = false;
1299         if (isOptical()) {
1300             unconfigureDisplay(view);
1301         }
1302         cancelAodSendFingerUpAction();
1303     }
1304 
1305     /**
1306      * Callback for fingerUp and fingerDown events.
1307      */
1308     public interface Callback {
1309         /**
1310          * Called onFingerUp events. Will only be called if the finger was previously down.
1311          */
onFingerUp()1312         void onFingerUp();
1313 
1314         /**
1315          * Called onFingerDown events.
1316          */
onFingerDown()1317         void onFingerDown();
1318     }
1319 }
1320