1 /*
2  * Copyright (C) 2018 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.hardware.biometrics.BiometricAuthenticator.TYPE_FACE;
20 import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT;
21 import static android.hardware.fingerprint.FingerprintSensorProperties.TYPE_REAR;
22 
23 import android.annotation.NonNull;
24 import android.annotation.Nullable;
25 import android.app.ActivityManager;
26 import android.app.ActivityTaskManager;
27 import android.app.TaskStackListener;
28 import android.content.BroadcastReceiver;
29 import android.content.Context;
30 import android.content.Intent;
31 import android.content.IntentFilter;
32 import android.content.res.Configuration;
33 import android.content.res.Resources;
34 import android.graphics.Point;
35 import android.graphics.Rect;
36 import android.hardware.SensorPrivacyManager;
37 import android.hardware.biometrics.BiometricAuthenticator.Modality;
38 import android.hardware.biometrics.BiometricConstants;
39 import android.hardware.biometrics.BiometricManager.Authenticators;
40 import android.hardware.biometrics.BiometricPrompt;
41 import android.hardware.biometrics.BiometricStateListener;
42 import android.hardware.biometrics.IBiometricContextListener;
43 import android.hardware.biometrics.IBiometricSysuiReceiver;
44 import android.hardware.biometrics.PromptInfo;
45 import android.hardware.display.DisplayManager;
46 import android.hardware.face.FaceManager;
47 import android.hardware.face.FaceSensorPropertiesInternal;
48 import android.hardware.face.IFaceAuthenticatorsRegisteredCallback;
49 import android.hardware.fingerprint.FingerprintManager;
50 import android.hardware.fingerprint.FingerprintSensorProperties;
51 import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
52 import android.hardware.fingerprint.IFingerprintAuthenticatorsRegisteredCallback;
53 import android.hardware.fingerprint.IUdfpsRefreshRateRequestCallback;
54 import android.os.Handler;
55 import android.os.RemoteException;
56 import android.os.UserManager;
57 import android.util.Log;
58 import android.util.RotationUtils;
59 import android.util.SparseBooleanArray;
60 import android.view.Display;
61 import android.view.DisplayInfo;
62 import android.view.MotionEvent;
63 import android.view.WindowManager;
64 
65 import com.android.internal.R;
66 import com.android.internal.annotations.VisibleForTesting;
67 import com.android.internal.jank.InteractionJankMonitor;
68 import com.android.internal.os.SomeArgs;
69 import com.android.internal.widget.LockPatternUtils;
70 import com.android.systemui.CoreStartable;
71 import com.android.systemui.biometrics.domain.interactor.LogContextInteractor;
72 import com.android.systemui.biometrics.domain.interactor.PromptSelectorInteractor;
73 import com.android.systemui.biometrics.shared.model.UdfpsOverlayParams;
74 import com.android.systemui.biometrics.ui.viewmodel.CredentialViewModel;
75 import com.android.systemui.biometrics.ui.viewmodel.PromptViewModel;
76 import com.android.systemui.dagger.SysUISingleton;
77 import com.android.systemui.dagger.qualifiers.Application;
78 import com.android.systemui.dagger.qualifiers.Background;
79 import com.android.systemui.dagger.qualifiers.Main;
80 import com.android.systemui.doze.DozeReceiver;
81 import com.android.systemui.keyguard.WakefulnessLifecycle;
82 import com.android.systemui.keyguard.data.repository.BiometricType;
83 import com.android.systemui.log.core.LogLevel;
84 import com.android.systemui.statusbar.CommandQueue;
85 import com.android.systemui.statusbar.VibratorHelper;
86 import com.android.systemui.statusbar.policy.ConfigurationController;
87 import com.android.systemui.util.concurrency.DelayableExecutor;
88 import com.android.systemui.util.concurrency.Execution;
89 
90 import dagger.Lazy;
91 
92 import kotlin.Unit;
93 
94 import kotlinx.coroutines.CoroutineScope;
95 import kotlinx.coroutines.Job;
96 
97 import java.io.PrintWriter;
98 import java.util.ArrayList;
99 import java.util.Arrays;
100 import java.util.HashMap;
101 import java.util.HashSet;
102 import java.util.List;
103 import java.util.Map;
104 import java.util.Objects;
105 import java.util.Set;
106 
107 import javax.inject.Inject;
108 import javax.inject.Provider;
109 
110 /**
111  * Receives messages sent from {@link com.android.server.biometrics.BiometricService} and shows the
112  * appropriate biometric UI (e.g. BiometricDialogView).
113  *
114  * Also coordinates biometric-related things, such as UDFPS, with
115  * {@link com.android.keyguard.KeyguardUpdateMonitor}
116  */
117 @SysUISingleton
118 public class AuthController implements
119         CoreStartable,
120         ConfigurationController.ConfigurationListener,
121         CommandQueue.Callbacks,
122         AuthDialogCallback,
123         DozeReceiver {
124 
125     private static final String TAG = "AuthController";
126     private static final boolean DEBUG = true;
127     private static final int SENSOR_PRIVACY_DELAY = 500;
128 
129     private final Handler mHandler;
130     private final Context mContext;
131     private final Execution mExecution;
132     private final CommandQueue mCommandQueue;
133     private final ActivityTaskManager mActivityTaskManager;
134     @Nullable private final FingerprintManager mFingerprintManager;
135     @Nullable private final FaceManager mFaceManager;
136     private final Provider<UdfpsController> mUdfpsControllerFactory;
137     private final CoroutineScope mApplicationCoroutineScope;
138     private Job mBiometricContextListenerJob = null;
139 
140     // TODO: these should be migrated out once ready
141     @NonNull private final Provider<PromptSelectorInteractor> mPromptSelectorInteractor;
142     @NonNull private final Provider<CredentialViewModel> mCredentialViewModelProvider;
143     @NonNull private final Provider<PromptViewModel> mPromptViewModelProvider;
144     @NonNull private final Lazy<LogContextInteractor> mLogContextInteractor;
145 
146     private final Display mDisplay;
147     private float mScaleFactor = 1f;
148     @Nullable private Point mFingerprintSensorLocation;
149     @Nullable private Rect mUdfpsBounds;
150     private final Set<Callback> mCallbacks = new HashSet<>();
151 
152     // TODO: These should just be saved from onSaveState
153     private SomeArgs mCurrentDialogArgs;
154     @VisibleForTesting
155     AuthDialog mCurrentDialog;
156 
157     @NonNull private final WindowManager mWindowManager;
158     @NonNull private final DisplayManager mDisplayManager;
159     @Nullable private UdfpsController mUdfpsController;
160     @Nullable private UdfpsOverlayParams mUdfpsOverlayParams;
161     @Nullable private IUdfpsRefreshRateRequestCallback mUdfpsRefreshRateRequestCallback;
162     @NonNull private Lazy<UdfpsLogger> mUdfpsLogger;
163     @VisibleForTesting IBiometricSysuiReceiver mReceiver;
164     @VisibleForTesting @NonNull final BiometricDisplayListener mOrientationListener;
165     @Nullable private final List<FaceSensorPropertiesInternal> mFaceProps;
166     @Nullable private List<FingerprintSensorPropertiesInternal> mFpProps;
167     @Nullable private List<FingerprintSensorPropertiesInternal> mUdfpsProps;
168     @Nullable private List<FingerprintSensorPropertiesInternal> mSidefpsProps;
169 
170     @NonNull private final Map<Integer, Boolean> mFpEnrolledForUser = new HashMap<>();
171     @NonNull private final SparseBooleanArray mUdfpsEnrolledForUser;
172     @NonNull private final SparseBooleanArray mFaceEnrolledForUser;
173     @NonNull private final SparseBooleanArray mSfpsEnrolledForUser;
174     @NonNull private final SensorPrivacyManager mSensorPrivacyManager;
175     private final WakefulnessLifecycle mWakefulnessLifecycle;
176     private final AuthDialogPanelInteractionDetector mPanelInteractionDetector;
177     private boolean mAllFingerprintAuthenticatorsRegistered;
178     @NonNull private final UserManager mUserManager;
179     @NonNull private final LockPatternUtils mLockPatternUtils;
180     @NonNull private final InteractionJankMonitor mInteractionJankMonitor;
181     @NonNull private final UdfpsUtils mUdfpsUtils;
182     private final @Background DelayableExecutor mBackgroundExecutor;
183     private final DisplayInfo mCachedDisplayInfo = new DisplayInfo();
184     @NonNull private final VibratorHelper mVibratorHelper;
185 
186     @VisibleForTesting
187     final TaskStackListener mTaskStackListener = new TaskStackListener() {
188         @Override
189         public void onTaskStackChanged() {
190             if (!isOwnerInForeground()) {
191                 mHandler.post(AuthController.this::cancelIfOwnerIsNotInForeground);
192             }
193         }
194     };
195 
196     @VisibleForTesting
197     final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
198         @Override
199         public void onReceive(Context context, Intent intent) {
200             if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(intent.getAction())) {
201                 String reason = intent.getStringExtra("reason");
202                 reason = (reason != null) ? reason : "unknown";
203                 closeDialog(reason);
204             }
205         }
206     };
207 
closeDialog(String reason)208     private void closeDialog(String reason) {
209         if (isShowing()) {
210             Log.i(TAG, "Close BP, reason :" + reason);
211             mCurrentDialog.dismissWithoutCallback(true /* animate */);
212             mCurrentDialog = null;
213 
214             for (Callback cb : mCallbacks) {
215                 cb.onBiometricPromptDismissed();
216             }
217 
218             try {
219                 if (mReceiver != null) {
220                     mReceiver.onDialogDismissed(BiometricPrompt.DISMISSED_REASON_USER_CANCEL,
221                             null /* credentialAttestation */);
222                     mReceiver = null;
223                 }
224             } catch (RemoteException e) {
225                 Log.e(TAG, "Remote exception", e);
226             }
227         }
228     }
229 
isOwnerInForeground()230     private boolean isOwnerInForeground() {
231         if (mCurrentDialog != null) {
232             final String clientPackage = mCurrentDialog.getOpPackageName();
233             final List<ActivityManager.RunningTaskInfo> runningTasks =
234                     mActivityTaskManager.getTasks(1);
235             if (!runningTasks.isEmpty()) {
236                 final String topPackage = runningTasks.get(0).topActivity.getPackageName();
237                 if (!topPackage.contentEquals(clientPackage)
238                         && !Utils.isSystem(mContext, clientPackage)) {
239                     Log.w(TAG, "Evicting client due to: " + topPackage);
240                     return false;
241                 }
242             }
243         }
244         return true;
245     }
246 
cancelIfOwnerIsNotInForeground()247     private void cancelIfOwnerIsNotInForeground() {
248         mExecution.assertIsMainThread();
249         if (mCurrentDialog != null) {
250             try {
251                 mCurrentDialog.dismissWithoutCallback(true /* animate */);
252                 mCurrentDialog = null;
253 
254                 for (Callback cb : mCallbacks) {
255                     cb.onBiometricPromptDismissed();
256                 }
257 
258                 if (mReceiver != null) {
259                     mReceiver.onDialogDismissed(
260                             BiometricPrompt.DISMISSED_REASON_USER_CANCEL,
261                             null /* credentialAttestation */);
262                     mReceiver = null;
263                 }
264             } catch (RemoteException e) {
265                 Log.e(TAG, "Remote exception", e);
266             }
267         }
268     }
269 
270     /**
271      * Whether all fingerprint authentictors have been registered.
272      */
areAllFingerprintAuthenticatorsRegistered()273     public boolean areAllFingerprintAuthenticatorsRegistered() {
274         return mAllFingerprintAuthenticatorsRegistered;
275     }
276 
handleAllFingerprintAuthenticatorsRegistered( List<FingerprintSensorPropertiesInternal> sensors)277     private void handleAllFingerprintAuthenticatorsRegistered(
278             List<FingerprintSensorPropertiesInternal> sensors) {
279         mExecution.assertIsMainThread();
280         if (DEBUG) {
281             Log.d(TAG, "handleAllFingerprintAuthenticatorsRegistered | sensors: "
282                     + Arrays.toString(sensors.toArray()));
283         }
284         mAllFingerprintAuthenticatorsRegistered = true;
285         mFpProps = sensors;
286 
287         List<FingerprintSensorPropertiesInternal> udfpsProps = new ArrayList<>();
288         List<FingerprintSensorPropertiesInternal> sidefpsProps = new ArrayList<>();
289         for (FingerprintSensorPropertiesInternal props : mFpProps) {
290             if (props.isAnyUdfpsType()) {
291                 udfpsProps.add(props);
292             }
293             if (props.isAnySidefpsType()) {
294                 sidefpsProps.add(props);
295             }
296         }
297 
298         mUdfpsProps = !udfpsProps.isEmpty() ? udfpsProps : null;
299         if (mUdfpsProps != null) {
300             mUdfpsController = mUdfpsControllerFactory.get();
301             mUdfpsController.addCallback(new UdfpsController.Callback() {
302                 @Override
303                 public void onFingerUp() {
304                 }
305 
306                 @Override
307                 public void onFingerDown() {
308                     if (mCurrentDialog != null) {
309                         mCurrentDialog.onPointerDown();
310                     }
311                 }
312             });
313             mUdfpsController.setAuthControllerUpdateUdfpsLocation(this::updateUdfpsLocation);
314             mUdfpsController.setUdfpsDisplayMode(new UdfpsDisplayMode(mContext, mExecution,
315                     this, mUdfpsLogger.get()));
316             mUdfpsBounds = mUdfpsProps.get(0).getLocation().getRect();
317         }
318         mSidefpsProps = !sidefpsProps.isEmpty() ? sidefpsProps : null;
319         mFingerprintManager.registerBiometricStateListener(new BiometricStateListener() {
320             @Override
321             public void onEnrollmentsChanged(int userId, int sensorId, boolean hasEnrollments) {
322                 mHandler.post(() -> handleEnrollmentsChanged(
323                         TYPE_FINGERPRINT, userId, sensorId, hasEnrollments));
324             }
325         });
326         updateSensorLocations();
327 
328         for (Callback cb : mCallbacks) {
329             cb.onAllAuthenticatorsRegistered(TYPE_FINGERPRINT);
330         }
331     }
332 
handleAllFaceAuthenticatorsRegistered(List<FaceSensorPropertiesInternal> sensors)333     private void handleAllFaceAuthenticatorsRegistered(List<FaceSensorPropertiesInternal> sensors) {
334         mExecution.assertIsMainThread();
335         if (DEBUG) {
336             Log.d(TAG, "handleAllFaceAuthenticatorsRegistered | sensors: " + Arrays.toString(
337                     sensors.toArray()));
338         }
339 
340         mFaceManager.registerBiometricStateListener(new BiometricStateListener() {
341             @Override
342             public void onEnrollmentsChanged(int userId, int sensorId, boolean hasEnrollments) {
343                 mHandler.post(() -> handleEnrollmentsChanged(
344                         TYPE_FACE, userId, sensorId, hasEnrollments));
345             }
346         });
347 
348         for (Callback cb : mCallbacks) {
349             cb.onAllAuthenticatorsRegistered(TYPE_FACE);
350         }
351     }
352 
handleEnrollmentsChanged(@odality int modality, int userId, int sensorId, boolean hasEnrollments)353     private void handleEnrollmentsChanged(@Modality int modality, int userId, int sensorId,
354             boolean hasEnrollments) {
355         mExecution.assertIsMainThread();
356         Log.d(TAG, "handleEnrollmentsChanged, userId: " + userId + ", sensorId: " + sensorId
357                 + ", hasEnrollments: " + hasEnrollments);
358         BiometricType sensorBiometricType = BiometricType.UNKNOWN;
359         if (mFpProps != null) {
360             for (FingerprintSensorPropertiesInternal prop: mFpProps) {
361                 if (prop.sensorId == sensorId) {
362                     mFpEnrolledForUser.put(userId, hasEnrollments);
363                     if (prop.isAnyUdfpsType()) {
364                         sensorBiometricType = BiometricType.UNDER_DISPLAY_FINGERPRINT;
365                         mUdfpsEnrolledForUser.put(userId, hasEnrollments);
366                     } else if (prop.isAnySidefpsType()) {
367                         sensorBiometricType = BiometricType.SIDE_FINGERPRINT;
368                         mSfpsEnrolledForUser.put(userId, hasEnrollments);
369                     } else if (prop.sensorType == TYPE_REAR) {
370                         sensorBiometricType = BiometricType.REAR_FINGERPRINT;
371                     }
372                     break;
373                 }
374             }
375         }
376         if (mFaceProps == null) {
377             Log.d(TAG, "handleEnrollmentsChanged, mFaceProps is null");
378         } else {
379             for (FaceSensorPropertiesInternal prop : mFaceProps) {
380                 if (prop.sensorId == sensorId) {
381                     mFaceEnrolledForUser.put(userId, hasEnrollments);
382                     sensorBiometricType = BiometricType.FACE;
383                     break;
384                 }
385             }
386         }
387         for (Callback cb : mCallbacks) {
388             cb.onEnrollmentsChanged(modality);
389             cb.onEnrollmentsChanged(sensorBiometricType, userId, hasEnrollments);
390         }
391     }
392 
393     /**
394      * Adds a callback. See {@link Callback}.
395      */
addCallback(@onNull Callback callback)396     public void addCallback(@NonNull Callback callback) {
397         mCallbacks.add(callback);
398     }
399 
400     /**
401      * Removes a callback. See {@link Callback}.
402      */
removeCallback(@onNull Callback callback)403     public void removeCallback(@NonNull Callback callback) {
404         mCallbacks.remove(callback);
405     }
406 
407     @Override
dozeTimeTick()408     public void dozeTimeTick() {
409         if (mUdfpsController != null) {
410             mUdfpsController.dozeTimeTick();
411         }
412     }
413 
414     @Override
onTryAgainPressed(long requestId)415     public void onTryAgainPressed(long requestId) {
416         final IBiometricSysuiReceiver receiver = getCurrentReceiver(requestId);
417         if (receiver == null) {
418             Log.w(TAG, "Skip onTryAgainPressed");
419             return;
420         }
421 
422         try {
423             receiver.onTryAgainPressed();
424         } catch (RemoteException e) {
425             Log.e(TAG, "RemoteException when handling try again", e);
426         }
427     }
428 
429     @Override
onDeviceCredentialPressed(long requestId)430     public void onDeviceCredentialPressed(long requestId) {
431         final IBiometricSysuiReceiver receiver = getCurrentReceiver(requestId);
432         if (receiver == null) {
433             Log.w(TAG, "Skip onDeviceCredentialPressed");
434             return;
435         }
436 
437         try {
438             receiver.onDeviceCredentialPressed();
439         } catch (RemoteException e) {
440             Log.e(TAG, "RemoteException when handling credential button", e);
441         }
442     }
443 
444     @Override
onSystemEvent(int event, long requestId)445     public void onSystemEvent(int event, long requestId) {
446         final IBiometricSysuiReceiver receiver = getCurrentReceiver(requestId);
447         if (receiver == null) {
448             Log.w(TAG, "Skip onSystemEvent");
449             return;
450         }
451 
452         try {
453             receiver.onSystemEvent(event);
454         } catch (RemoteException e) {
455             Log.e(TAG, "RemoteException when sending system event", e);
456         }
457     }
458 
459     @Override
onDialogAnimatedIn(long requestId, boolean startFingerprintNow)460     public void onDialogAnimatedIn(long requestId, boolean startFingerprintNow) {
461         final IBiometricSysuiReceiver receiver = getCurrentReceiver(requestId);
462         if (receiver == null) {
463             Log.w(TAG, "Skip onDialogAnimatedIn");
464             return;
465         }
466 
467         try {
468             receiver.onDialogAnimatedIn(startFingerprintNow);
469         } catch (RemoteException e) {
470             Log.e(TAG, "RemoteException when sending onDialogAnimatedIn", e);
471         }
472     }
473 
474     @Override
onStartFingerprintNow(long requestId)475     public void onStartFingerprintNow(long requestId) {
476         final IBiometricSysuiReceiver receiver = getCurrentReceiver(requestId);
477         if (receiver == null) {
478             Log.e(TAG, "onStartUdfpsNow: Receiver is null");
479             return;
480         }
481 
482         try {
483             receiver.onStartFingerprintNow();
484         } catch (RemoteException e) {
485             Log.e(TAG, "RemoteException when sending onDialogAnimatedIn", e);
486         }
487     }
488 
489     @Nullable
getCurrentReceiver(long requestId)490     private IBiometricSysuiReceiver getCurrentReceiver(long requestId) {
491         if (!isRequestIdValid(requestId)) {
492             return null;
493         }
494 
495         if (mReceiver == null) {
496             Log.w(TAG, "getCurrentReceiver: Receiver is null");
497         }
498 
499         return mReceiver;
500     }
501 
isRequestIdValid(long requestId)502     private boolean isRequestIdValid(long requestId) {
503         if (mCurrentDialog == null) {
504             Log.w(TAG, "shouldNotifyReceiver: dialog already gone");
505             return false;
506         }
507 
508         if (requestId != mCurrentDialog.getRequestId()) {
509             Log.w(TAG, "shouldNotifyReceiver: requestId doesn't match");
510             return false;
511         }
512 
513         return true;
514     }
515 
516     @Override
onDismissed(@ismissedReason int reason, @Nullable byte[] credentialAttestation, long requestId)517     public void onDismissed(@DismissedReason int reason,
518                             @Nullable byte[] credentialAttestation, long requestId) {
519 
520         if (mCurrentDialog != null && requestId != mCurrentDialog.getRequestId()) {
521             Log.w(TAG, "requestId doesn't match, skip onDismissed");
522             return;
523         }
524 
525         switch (reason) {
526             case AuthDialogCallback.DISMISSED_USER_CANCELED:
527                 sendResultAndCleanUp(BiometricPrompt.DISMISSED_REASON_USER_CANCEL,
528                         credentialAttestation);
529                 break;
530 
531             case AuthDialogCallback.DISMISSED_BUTTON_NEGATIVE:
532                 sendResultAndCleanUp(BiometricPrompt.DISMISSED_REASON_NEGATIVE,
533                         credentialAttestation);
534                 break;
535 
536             case AuthDialogCallback.DISMISSED_BUTTON_POSITIVE:
537                 sendResultAndCleanUp(BiometricPrompt.DISMISSED_REASON_BIOMETRIC_CONFIRMED,
538                         credentialAttestation);
539                 break;
540 
541             case AuthDialogCallback.DISMISSED_BIOMETRIC_AUTHENTICATED:
542                 sendResultAndCleanUp(
543                         BiometricPrompt.DISMISSED_REASON_BIOMETRIC_CONFIRM_NOT_REQUIRED,
544                         credentialAttestation);
545                 break;
546 
547             case AuthDialogCallback.DISMISSED_ERROR:
548                 sendResultAndCleanUp(BiometricPrompt.DISMISSED_REASON_ERROR,
549                         credentialAttestation);
550                 break;
551 
552             case AuthDialogCallback.DISMISSED_BY_SYSTEM_SERVER:
553                 sendResultAndCleanUp(BiometricPrompt.DISMISSED_REASON_SERVER_REQUESTED,
554                         credentialAttestation);
555                 break;
556 
557             case AuthDialogCallback.DISMISSED_CREDENTIAL_AUTHENTICATED:
558                 sendResultAndCleanUp(BiometricPrompt.DISMISSED_REASON_CREDENTIAL_CONFIRMED,
559                         credentialAttestation);
560                 break;
561 
562             case AuthDialogCallback.DISMISSED_BUTTON_CONTENT_VIEW_MORE_OPTIONS:
563                 sendResultAndCleanUp(
564                         BiometricPrompt.DISMISSED_REASON_CONTENT_VIEW_MORE_OPTIONS,
565                         credentialAttestation);
566                 break;
567             default:
568                 Log.e(TAG, "Unhandled reason: " + reason);
569                 break;
570         }
571     }
572 
573     @Override
handleShowGlobalActionsMenu()574     public void handleShowGlobalActionsMenu() {
575         closeDialog("PowerMenu shown");
576     }
577 
578     /**
579      * @return where the UDFPS exists on the screen in pixels in portrait mode.
580      */
getUdfpsLocation()581     @Nullable public Point getUdfpsLocation() {
582         if (mUdfpsController == null || mUdfpsBounds == null) {
583             return null;
584         }
585         return new Point(mUdfpsBounds.centerX(), mUdfpsBounds.centerY());
586     }
587 
588     /**
589      * @return the radius of UDFPS on the screen in pixels
590      */
getUdfpsRadius()591     public float getUdfpsRadius() {
592         if (mUdfpsController == null || mUdfpsBounds == null) {
593             return -1;
594         }
595         return mUdfpsBounds.height() / 2f;
596     }
597 
598     /**
599      * Gets the cached scale factor representing the user's current resolution / the stable
600      * (default) resolution.
601      */
getScaleFactor()602     public float getScaleFactor() {
603         return mScaleFactor;
604     }
605 
606     /**
607      * Updates the current display info and cached scale factor & sensor locations.
608      * Getting the display info is a relatively expensive call, so avoid superfluous calls.
609      */
updateSensorLocations()610     private void updateSensorLocations() {
611         mDisplay.getDisplayInfo(mCachedDisplayInfo);
612         mScaleFactor = mUdfpsUtils.getScaleFactor(mCachedDisplayInfo);
613         updateUdfpsLocation();
614         updateFingerprintLocation();
615     }
616     /**
617      * @return where the fingerprint sensor exists in pixels in its natural orientation.
618      * Devices without location configs will use the default value even if they don't have a
619      * fingerprint sensor.
620      *
621      * May return null if the fingerprint sensor isn't available yet.
622      */
getFingerprintSensorLocationInNaturalOrientation()623     @Nullable private Point getFingerprintSensorLocationInNaturalOrientation() {
624         if (getUdfpsLocation() != null) {
625             return getUdfpsLocation();
626         } else {
627             int xFpLocation = mCachedDisplayInfo.getNaturalWidth() / 2;
628             try {
629                 xFpLocation = mContext.getResources().getDimensionPixelSize(
630                         com.android.systemui.res.R.dimen
631                                 .physical_fingerprint_sensor_center_screen_location_x);
632             } catch (Resources.NotFoundException e) {
633             }
634 
635             return new Point(
636                     (int) (xFpLocation * mScaleFactor),
637                     (int) (mContext.getResources().getDimensionPixelSize(
638                             com.android.systemui.res.R.dimen
639                                     .physical_fingerprint_sensor_center_screen_location_y)
640                             * mScaleFactor)
641             );
642         }
643     }
644 
645     /**
646      * @return where the fingerprint sensor exists in pixels exists the current device orientation.
647      * Devices without location configs will use the default value even if they don't have a
648      * fingerprint sensor.
649      */
getFingerprintSensorLocation()650     @Nullable public Point getFingerprintSensorLocation() {
651         return mFingerprintSensorLocation;
652     }
653 
updateFingerprintLocation()654     private void updateFingerprintLocation() {
655         if (mFpProps == null) {
656             mFingerprintSensorLocation = null;
657         } else {
658             mFingerprintSensorLocation = rotateToCurrentOrientation(
659                     getFingerprintSensorLocationInNaturalOrientation(),
660                     mCachedDisplayInfo);
661         }
662 
663         for (final Callback cb : mCallbacks) {
664             cb.onFingerprintLocationChanged();
665         }
666     }
667 
668     /** Get FP sensor properties */
getFingerprintProperties()669     public @Nullable List<FingerprintSensorPropertiesInternal> getFingerprintProperties() {
670         return mFpProps;
671     }
672 
673     /**
674      * @param inOutPoint point on the display in pixels. Going in, represents the point
675      *                   in the device's natural orientation. Going out, represents
676      *                   the point in the display's current orientation.
677      * @param displayInfo currently display information to use to rotate the point
678      */
679     @VisibleForTesting
rotateToCurrentOrientation(Point inOutPoint, DisplayInfo displayInfo)680     protected Point rotateToCurrentOrientation(Point inOutPoint, DisplayInfo displayInfo) {
681         RotationUtils.rotatePoint(
682                 inOutPoint,
683                 displayInfo.rotation,
684                 displayInfo.getNaturalWidth(),
685                 displayInfo.getNaturalHeight()
686         );
687         return inOutPoint;
688     }
689 
690     /**
691      * Requests fingerprint scan.
692      *
693      * @param screenX X position of long press
694      * @param screenY Y position of long press
695      * @param major length of the major axis. See {@link MotionEvent#AXIS_TOOL_MAJOR}.
696      * @param minor length of the minor axis. See {@link MotionEvent#AXIS_TOOL_MINOR}.
697      */
onAodInterrupt(int screenX, int screenY, float major, float minor)698     public void onAodInterrupt(int screenX, int screenY, float major, float minor) {
699         if (mUdfpsController == null) {
700             return;
701         }
702         mUdfpsController.onAodInterrupt(screenX, screenY, major, minor);
703     }
704 
sendResultAndCleanUp(@ismissedReason int reason, @Nullable byte[] credentialAttestation)705     private void sendResultAndCleanUp(@DismissedReason int reason,
706             @Nullable byte[] credentialAttestation) {
707         if (mReceiver == null) {
708             Log.e(TAG, "sendResultAndCleanUp: Receiver is null");
709             return;
710         }
711 
712         try {
713             mReceiver.onDialogDismissed(reason, credentialAttestation);
714         } catch (RemoteException e) {
715             Log.w(TAG, "Remote exception", e);
716         }
717         onDialogDismissed(reason);
718     }
719     @Inject
AuthController(Context context, @Application CoroutineScope applicationCoroutineScope, Execution execution, CommandQueue commandQueue, ActivityTaskManager activityTaskManager, @NonNull WindowManager windowManager, @Nullable FingerprintManager fingerprintManager, @Nullable FaceManager faceManager, Provider<UdfpsController> udfpsControllerFactory, @NonNull DisplayManager displayManager, @NonNull WakefulnessLifecycle wakefulnessLifecycle, @NonNull AuthDialogPanelInteractionDetector panelInteractionDetector, @NonNull UserManager userManager, @NonNull LockPatternUtils lockPatternUtils, @NonNull Lazy<UdfpsLogger> udfpsLogger, @NonNull Lazy<LogContextInteractor> logContextInteractor, @NonNull Provider<PromptSelectorInteractor> promptSelectorInteractorProvider, @NonNull Provider<CredentialViewModel> credentialViewModelProvider, @NonNull Provider<PromptViewModel> promptViewModelProvider, @NonNull InteractionJankMonitor jankMonitor, @Main Handler handler, @Background DelayableExecutor bgExecutor, @NonNull UdfpsUtils udfpsUtils, @NonNull VibratorHelper vibratorHelper)720     public AuthController(Context context,
721             @Application CoroutineScope applicationCoroutineScope,
722             Execution execution,
723             CommandQueue commandQueue,
724             ActivityTaskManager activityTaskManager,
725             @NonNull WindowManager windowManager,
726             @Nullable FingerprintManager fingerprintManager,
727             @Nullable FaceManager faceManager,
728             Provider<UdfpsController> udfpsControllerFactory,
729             @NonNull DisplayManager displayManager,
730             @NonNull WakefulnessLifecycle wakefulnessLifecycle,
731             @NonNull AuthDialogPanelInteractionDetector panelInteractionDetector,
732             @NonNull UserManager userManager,
733             @NonNull LockPatternUtils lockPatternUtils,
734             @NonNull Lazy<UdfpsLogger> udfpsLogger,
735             @NonNull Lazy<LogContextInteractor> logContextInteractor,
736             @NonNull Provider<PromptSelectorInteractor> promptSelectorInteractorProvider,
737             @NonNull Provider<CredentialViewModel> credentialViewModelProvider,
738             @NonNull Provider<PromptViewModel> promptViewModelProvider,
739             @NonNull InteractionJankMonitor jankMonitor,
740             @Main Handler handler,
741             @Background DelayableExecutor bgExecutor,
742             @NonNull UdfpsUtils udfpsUtils,
743             @NonNull VibratorHelper vibratorHelper) {
744         mContext = context;
745         mExecution = execution;
746         mUserManager = userManager;
747         mLockPatternUtils = lockPatternUtils;
748         mHandler = handler;
749         mBackgroundExecutor = bgExecutor;
750         mCommandQueue = commandQueue;
751         mActivityTaskManager = activityTaskManager;
752         mFingerprintManager = fingerprintManager;
753         mFaceManager = faceManager;
754         mUdfpsControllerFactory = udfpsControllerFactory;
755         mUdfpsLogger = udfpsLogger;
756         mDisplayManager = displayManager;
757         mWindowManager = windowManager;
758         mInteractionJankMonitor = jankMonitor;
759         mUdfpsEnrolledForUser = new SparseBooleanArray();
760         mSfpsEnrolledForUser = new SparseBooleanArray();
761         mFaceEnrolledForUser = new SparseBooleanArray();
762         mUdfpsUtils = udfpsUtils;
763         mApplicationCoroutineScope = applicationCoroutineScope;
764         mVibratorHelper = vibratorHelper;
765 
766         mLogContextInteractor = logContextInteractor;
767         mPromptSelectorInteractor = promptSelectorInteractorProvider;
768         mPromptViewModelProvider = promptViewModelProvider;
769         mCredentialViewModelProvider = credentialViewModelProvider;
770 
771         mOrientationListener = new BiometricDisplayListener(
772                 context,
773                 mDisplayManager,
774                 mHandler,
775                 BiometricDisplayListener.SensorType.Generic.INSTANCE,
776                 () -> {
777                     onOrientationChanged();
778                     return Unit.INSTANCE;
779                 });
780 
781         mWakefulnessLifecycle = wakefulnessLifecycle;
782         mPanelInteractionDetector = panelInteractionDetector;
783 
784         mFaceProps = mFaceManager != null ? mFaceManager.getSensorPropertiesInternal() : null;
785 
786         mDisplay = mContext.getDisplay();
787         updateSensorLocations();
788 
789         IntentFilter filter = new IntentFilter();
790         filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
791         context.registerReceiver(mBroadcastReceiver, filter, Context.RECEIVER_EXPORTED_UNAUDITED);
792         mSensorPrivacyManager = context.getSystemService(SensorPrivacyManager.class);
793     }
794 
795     // TODO(b/229290039): UDFPS controller should manage its dimensions on its own. Remove this.
796     // This is not combined with updateFingerprintLocation because this is invoked directly from
797     // UdfpsController, only when it cares about a rotation change. The implications of calling
798     // updateFingerprintLocation in such a case are unclear.
updateUdfpsLocation()799     private void updateUdfpsLocation() {
800         if (mUdfpsController != null) {
801             final FingerprintSensorPropertiesInternal udfpsProp = mUdfpsProps.get(0);
802 
803             final Rect previousUdfpsBounds = mUdfpsBounds;
804             final UdfpsOverlayParams previousUdfpsOverlayParams = mUdfpsOverlayParams;
805 
806             mUdfpsBounds = udfpsProp.getLocation().getRect();
807             mUdfpsBounds.scale(mScaleFactor);
808 
809             final Rect overlayBounds = new Rect(
810                     0, /* left */
811                     mCachedDisplayInfo.getNaturalHeight() / 2, /* top */
812                     mCachedDisplayInfo.getNaturalWidth(), /* right */
813                     mCachedDisplayInfo.getNaturalHeight() /* bottom */);
814 
815             mUdfpsOverlayParams = new UdfpsOverlayParams(
816                     mUdfpsBounds,
817                     overlayBounds,
818                     mCachedDisplayInfo.getNaturalWidth(),
819                     mCachedDisplayInfo.getNaturalHeight(),
820                     mScaleFactor,
821                     mCachedDisplayInfo.rotation,
822                     udfpsProp.sensorType);
823 
824             mUdfpsController.updateOverlayParams(udfpsProp, mUdfpsOverlayParams);
825             if (!Objects.equals(previousUdfpsBounds, mUdfpsBounds) || !Objects.equals(
826                     previousUdfpsOverlayParams, mUdfpsOverlayParams)) {
827                 for (Callback cb : mCallbacks) {
828                     cb.onUdfpsLocationChanged(mUdfpsOverlayParams);
829                 }
830             }
831         }
832     }
833 
834     @SuppressWarnings("deprecation")
835     @Override
start()836     public void start() {
837         mCommandQueue.addCallback(this);
838 
839         if (mFingerprintManager != null) {
840             mFingerprintManager.addAuthenticatorsRegisteredCallback(
841                     new IFingerprintAuthenticatorsRegisteredCallback.Stub() {
842                         @Override
843                         public void onAllAuthenticatorsRegistered(
844                                 List<FingerprintSensorPropertiesInternal> sensors) {
845                             mHandler.post(() ->
846                                     handleAllFingerprintAuthenticatorsRegistered(sensors));
847                         }
848                     });
849         }
850         if (mFaceManager != null) {
851             mFaceManager.addAuthenticatorsRegisteredCallback(
852                     new IFaceAuthenticatorsRegisteredCallback.Stub() {
853                         @Override
854                         public void onAllAuthenticatorsRegistered(
855                                 List<FaceSensorPropertiesInternal> sensors) {
856                             mHandler.post(() ->
857                                     handleAllFaceAuthenticatorsRegistered(sensors));
858                         }
859                     }
860             );
861         }
862 
863         mActivityTaskManager.registerTaskStackListener(mTaskStackListener);
864         mOrientationListener.enable();
865         updateSensorLocations();
866     }
867 
868     @Override
setBiometricContextListener(IBiometricContextListener listener)869     public void setBiometricContextListener(IBiometricContextListener listener) {
870         if (mBiometricContextListenerJob != null) {
871             mBiometricContextListenerJob.cancel(null);
872         }
873         mBiometricContextListenerJob =
874                 mLogContextInteractor.get().addBiometricContextListener(listener);
875     }
876 
877     /**
878      * Stores the callback received from
879      * {@link com.android.server.display.mode.DisplayModeDirector}.
880      *
881      * DisplayModeDirector implements {@link IUdfpsRefreshRateRequestCallback}
882      * and registers it with this class by calling
883      * {@link CommandQueue#setUdfpsRefreshRateCallback(IUdfpsRefreshRateRequestCallback)}.
884      */
885     @Override
setUdfpsRefreshRateCallback(IUdfpsRefreshRateRequestCallback callback)886     public void setUdfpsRefreshRateCallback(IUdfpsRefreshRateRequestCallback callback) {
887         mUdfpsRefreshRateRequestCallback = callback;
888     }
889 
890     /**
891      * @return IUdfpsRefreshRateRequestCallback that can be set by DisplayModeDirector.
892      */
getUdfpsRefreshRateCallback()893     @Nullable public IUdfpsRefreshRateRequestCallback getUdfpsRefreshRateCallback() {
894         return mUdfpsRefreshRateRequestCallback;
895     }
896 
897     /**
898      * Requests (or stops requesting) the max refresh rate. This can override user settings
899      * for the max refresh rate.
900      */
requestMaxRefreshRate(boolean request)901     public void requestMaxRefreshRate(boolean request) throws RemoteException {
902         if (mUdfpsRefreshRateRequestCallback == null) {
903             mUdfpsLogger.get().log(
904                     "PreAuthRefreshRate",
905                     "skip request - refreshRateCallback is null",
906                     LogLevel.DEBUG
907             );
908             return;
909         }
910         mUdfpsLogger.get().requestMaxRefreshRate(request);
911         mUdfpsRefreshRateRequestCallback.onAuthenticationPossible(mContext.getDisplayId(), request);
912     }
913 
914     @Override
showAuthenticationDialog(PromptInfo promptInfo, IBiometricSysuiReceiver receiver, int[] sensorIds, boolean credentialAllowed, boolean requireConfirmation, int userId, long operationId, String opPackageName, long requestId)915     public void showAuthenticationDialog(PromptInfo promptInfo, IBiometricSysuiReceiver receiver,
916             int[] sensorIds, boolean credentialAllowed, boolean requireConfirmation,
917             int userId, long operationId, String opPackageName, long requestId) {
918         @Authenticators.Types final int authenticators = promptInfo.getAuthenticators();
919 
920         if (DEBUG) {
921             StringBuilder ids = new StringBuilder();
922             for (int sensorId : sensorIds) {
923                 ids.append(sensorId).append(" ");
924             }
925             Log.d(TAG, "showAuthenticationDialog, authenticators: " + authenticators
926                     + ", sensorIds: " + ids.toString()
927                     + ", credentialAllowed: " + credentialAllowed
928                     + ", requireConfirmation: " + requireConfirmation
929                     + ", operationId: " + operationId
930                     + ", requestId: " + requestId);
931         }
932         SomeArgs args = SomeArgs.obtain();
933         args.arg1 = promptInfo;
934         args.arg2 = receiver;
935         args.arg3 = sensorIds;
936         args.arg4 = credentialAllowed;
937         args.arg5 = requireConfirmation;
938         args.argi1 = userId;
939         args.arg6 = opPackageName;
940         args.argl1 = operationId;
941         args.argl2 = requestId;
942 
943         boolean skipAnimation = false;
944         if (mCurrentDialog != null) {
945             Log.w(TAG, "mCurrentDialog: " + mCurrentDialog);
946             skipAnimation = true;
947         }
948 
949         showDialog(args, skipAnimation, mPromptViewModelProvider.get());
950     }
951 
952     /**
953      * Only called via BiometricService for the biometric prompt. Will not be called for
954      * authentication directly requested through FingerprintManager. For
955      * example, KeyguardUpdateMonitor has its own {@link FingerprintManager.AuthenticationCallback}.
956      */
957     @Override
onBiometricAuthenticated(@odality int modality)958     public void onBiometricAuthenticated(@Modality int modality) {
959         if (DEBUG) Log.d(TAG, "onBiometricAuthenticated: ");
960 
961         if (mCurrentDialog != null) {
962             mCurrentDialog.onAuthenticationSucceeded(modality);
963         } else {
964             Log.w(TAG, "onBiometricAuthenticated callback but dialog gone");
965         }
966     }
967 
968     @Override
onBiometricHelp(@odality int modality, String message)969     public void onBiometricHelp(@Modality int modality, String message) {
970         if (DEBUG) Log.d(TAG, "onBiometricHelp: " + message);
971 
972         if (mCurrentDialog != null) {
973             mCurrentDialog.onHelp(modality, message);
974         } else {
975             Log.w(TAG, "onBiometricHelp callback but dialog gone");
976         }
977     }
978 
979     @Nullable
getUdfpsProps()980     public List<FingerprintSensorPropertiesInternal> getUdfpsProps() {
981         return mUdfpsProps;
982     }
983 
984     @Nullable
getSfpsProps()985     public List<FingerprintSensorPropertiesInternal> getSfpsProps() {
986         return mSidefpsProps;
987     }
988 
989     /**
990      * @return true if udfps HW is supported on this device. Can return true even if the user has
991      * not enrolled udfps. This may be false if called before onAllAuthenticatorsRegistered.
992      */
isUdfpsSupported()993     public boolean isUdfpsSupported() {
994         return getUdfpsProps() != null && !getUdfpsProps().isEmpty();
995     }
996 
997     /**
998      * @return true if ultrasonic udfps HW is supported on this device. Can return true even if
999      * the user has not enrolled udfps. This may be false if called before
1000      * onAllAuthenticatorsRegistered.
1001      */
isUltrasonicUdfpsSupported()1002     public boolean isUltrasonicUdfpsSupported() {
1003         return getUdfpsProps() != null && !getUdfpsProps().isEmpty() && getUdfpsProps()
1004                 .get(0).isUltrasonicUdfps();
1005     }
1006 
1007     /**
1008      * @return true if sfps HW is supported on this device. Can return true even if the user has
1009      * not enrolled sfps. This may be false if called before onAllAuthenticatorsRegistered.
1010      */
isSfpsSupported()1011     public boolean isSfpsSupported() {
1012         return getSfpsProps() != null && !getSfpsProps().isEmpty();
1013     }
1014 
1015     /**
1016      * @return true if rear fps HW is supported on this device. Can return true even if the user has
1017      * not enrolled sfps. This may be false if called before onAllAuthenticatorsRegistered.
1018      */
isRearFpsSupported()1019     public boolean isRearFpsSupported() {
1020         if (mFpProps != null) {
1021             for (FingerprintSensorPropertiesInternal prop: mFpProps) {
1022                 if (prop.sensorType == TYPE_REAR) {
1023                     return true;
1024                 }
1025             }
1026         }
1027         return false;
1028     }
1029 
getNotRecognizedString(@odality int modality)1030     private String getNotRecognizedString(@Modality int modality) {
1031         final int messageRes;
1032         final int userId = mCurrentDialogArgs.argi1;
1033         if (isFaceAuthEnrolled(userId) && isFingerprintEnrolled(userId)) {
1034             messageRes = modality == TYPE_FACE
1035                     ? R.string.fingerprint_dialog_use_fingerprint_instead
1036                     : R.string.fingerprint_error_not_match;
1037         } else {
1038             messageRes = R.string.biometric_not_recognized;
1039         }
1040         return mContext.getString(messageRes);
1041     }
1042 
getErrorString(@odality int modality, int error, int vendorCode)1043     private String getErrorString(@Modality int modality, int error, int vendorCode) {
1044         switch (modality) {
1045             case TYPE_FACE:
1046                 return FaceManager.getErrorString(mContext, error, vendorCode);
1047 
1048             case TYPE_FINGERPRINT:
1049                 return FingerprintManager.getErrorString(mContext, error, vendorCode);
1050 
1051             default:
1052                 return "";
1053         }
1054     }
1055 
1056     /**
1057      * Only called via BiometricService for the biometric prompt. Will not be called for
1058      * authentication directly requested through FingerprintManager. For
1059      * example, KeyguardUpdateMonitor has its own {@link FingerprintManager.AuthenticationCallback}.
1060      */
1061     @Override
onBiometricError(@odality int modality, int error, int vendorCode)1062     public void onBiometricError(@Modality int modality, int error, int vendorCode) {
1063         if (DEBUG) {
1064             Log.d(TAG, String.format("onBiometricError(%d, %d, %d)", modality, error, vendorCode));
1065         }
1066 
1067         final boolean isLockout = (error == BiometricConstants.BIOMETRIC_ERROR_LOCKOUT)
1068                 || (error == BiometricConstants.BIOMETRIC_ERROR_LOCKOUT_PERMANENT);
1069 
1070         boolean isCameraPrivacyEnabled = false;
1071         if (error == BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE
1072                 && mSensorPrivacyManager.isSensorPrivacyEnabled(
1073                 SensorPrivacyManager.TOGGLE_TYPE_SOFTWARE, SensorPrivacyManager.Sensors.CAMERA)) {
1074             isCameraPrivacyEnabled = true;
1075         }
1076         // TODO(b/141025588): Create separate methods for handling hard and soft errors.
1077         final boolean isSoftError = (error == BiometricConstants.BIOMETRIC_PAUSED_REJECTED
1078                 || error == BiometricConstants.BIOMETRIC_ERROR_TIMEOUT
1079                 || error == BiometricConstants.BIOMETRIC_ERROR_RE_ENROLL
1080                 || isCameraPrivacyEnabled);
1081         if (mCurrentDialog != null) {
1082             if (mCurrentDialog.isAllowDeviceCredentials() && isLockout) {
1083                 if (DEBUG) Log.d(TAG, "onBiometricError, lockout");
1084                 mCurrentDialog.animateToCredentialUI(true /* isError */);
1085             } else if (isSoftError) {
1086                 final String errorMessage = (error == BiometricConstants.BIOMETRIC_PAUSED_REJECTED
1087                         || error == BiometricConstants.BIOMETRIC_ERROR_TIMEOUT)
1088                         ? getNotRecognizedString(modality)
1089                         : getErrorString(modality, error, vendorCode);
1090                 if (DEBUG) Log.d(TAG, "onBiometricError, soft error: " + errorMessage);
1091                 // The camera privacy error can return before the prompt initializes its state,
1092                 // causing the prompt to appear to endlessly authenticate. Add a small delay
1093                 // to stop this.
1094                 if (isCameraPrivacyEnabled) {
1095                     mHandler.postDelayed(() -> {
1096                         mCurrentDialog.onAuthenticationFailed(modality,
1097                                 mContext.getString(R.string.face_sensor_privacy_enabled));
1098                     }, SENSOR_PRIVACY_DELAY);
1099                 } else {
1100                     mCurrentDialog.onAuthenticationFailed(modality, errorMessage);
1101                 }
1102             } else {
1103                 final String errorMessage = getErrorString(modality, error, vendorCode);
1104                 if (DEBUG) Log.d(TAG, "onBiometricError, hard error: " + errorMessage);
1105                 mCurrentDialog.onError(modality, errorMessage);
1106             }
1107 
1108         } else {
1109             Log.w(TAG, "onBiometricError callback but dialog is gone");
1110         }
1111     }
1112 
1113     @Override
hideAuthenticationDialog(long requestId)1114     public void hideAuthenticationDialog(long requestId) {
1115         if (DEBUG) Log.d(TAG, "hideAuthenticationDialog: " + mCurrentDialog);
1116 
1117         if (mCurrentDialog == null) {
1118             // Could be possible if the caller canceled authentication after credential success
1119             // but before the client was notified.
1120             if (DEBUG) Log.d(TAG, "dialog already gone");
1121             return;
1122         }
1123         if (requestId != mCurrentDialog.getRequestId()) {
1124             Log.w(TAG, "ignore - ids do not match: " + requestId + " current: "
1125                     + mCurrentDialog.getRequestId());
1126             return;
1127         }
1128 
1129         mCurrentDialog.dismissFromSystemServer();
1130         for (Callback cb : mCallbacks) {
1131             cb.onBiometricPromptDismissed();
1132         }
1133 
1134         // BiometricService will have already sent the callback to the client in this case.
1135         // This avoids a round trip to SystemUI. So, just dismiss the dialog and we're done.
1136         mCurrentDialog = null;
1137     }
1138 
1139     /**
1140      * Whether the user's finger is currently on udfps attempting to authenticate.
1141      */
isUdfpsFingerDown()1142     public boolean isUdfpsFingerDown() {
1143         if (mUdfpsController == null)  {
1144             return false;
1145         }
1146 
1147         return mUdfpsController.isFingerDown();
1148     }
1149 
1150     /**
1151      * Whether the passed userId has enrolled face auth.
1152      */
isFaceAuthEnrolled(int userId)1153     public boolean isFaceAuthEnrolled(int userId) {
1154         if (mFaceProps == null) {
1155             return false;
1156         }
1157 
1158         return mFaceEnrolledForUser.get(userId);
1159     }
1160 
1161     /**
1162      * Does the provided user have at least one optical udfps fingerprint enrolled?
1163      */
isOpticalUdfpsEnrolled(int userId)1164     public boolean isOpticalUdfpsEnrolled(int userId) {
1165         return isUdfpsEnrolled(userId)
1166                 && mUdfpsProps != null
1167                 && mUdfpsProps.get(0).sensorType == FingerprintSensorProperties.TYPE_UDFPS_OPTICAL;
1168     }
1169 
1170     /**
1171      * Whether the passed userId has enrolled UDFPS.
1172      */
isUdfpsEnrolled(int userId)1173     public boolean isUdfpsEnrolled(int userId) {
1174         if (mUdfpsController == null) {
1175             return false;
1176         }
1177 
1178         return mUdfpsEnrolledForUser.get(userId);
1179     }
1180 
1181     /**
1182      * Whether the passed userId has enrolled SFPS.
1183      */
isSfpsEnrolled(int userId)1184     public boolean isSfpsEnrolled(int userId) {
1185         if (mSidefpsProps == null) {
1186             return false;
1187         }
1188 
1189         return mSfpsEnrolledForUser.get(userId);
1190     }
1191 
1192     /** If BiometricPrompt is currently being shown to the user. */
isShowing()1193     public boolean isShowing() {
1194         return mCurrentDialog != null;
1195     }
1196 
1197     /**
1198      * Whether the passed userId has enrolled at least one fingerprint.
1199      */
isFingerprintEnrolled(int userId)1200     public boolean isFingerprintEnrolled(int userId) {
1201         return mFpEnrolledForUser.getOrDefault(userId, false);
1202     }
1203 
showDialog(SomeArgs args, boolean skipAnimation, @Nullable PromptViewModel viewModel)1204     private void showDialog(SomeArgs args, boolean skipAnimation,
1205             @Nullable PromptViewModel viewModel) {
1206         mCurrentDialogArgs = args;
1207 
1208         final PromptInfo promptInfo = (PromptInfo) args.arg1;
1209         final int[] sensorIds = (int[]) args.arg3;
1210 
1211         // TODO(b/251476085): remove these unused parameters (replaced with SSOT elsewhere)
1212         final boolean credentialAllowed = (boolean) args.arg4;
1213         final boolean requireConfirmation = (boolean) args.arg5;
1214 
1215         final int userId = args.argi1;
1216         final String opPackageName = (String) args.arg6;
1217         final long operationId = args.argl1;
1218         final long requestId = args.argl2;
1219 
1220         // Create a new dialog but do not replace the current one yet.
1221         final AuthDialog newDialog = buildDialog(
1222                 mBackgroundExecutor,
1223                 promptInfo,
1224                 requireConfirmation,
1225                 userId,
1226                 sensorIds,
1227                 opPackageName,
1228                 skipAnimation,
1229                 operationId,
1230                 requestId,
1231                 mWakefulnessLifecycle,
1232                 mPanelInteractionDetector,
1233                 mUserManager,
1234                 mLockPatternUtils,
1235                 viewModel);
1236 
1237         if (newDialog == null) {
1238             Log.e(TAG, "Unsupported type configuration");
1239             return;
1240         }
1241 
1242         if (DEBUG) {
1243             Log.d(TAG, "userId: " + userId
1244                     + " mCurrentDialog: " + mCurrentDialog
1245                     + " newDialog: " + newDialog);
1246         }
1247 
1248         if (mCurrentDialog != null) {
1249             // If somehow we're asked to show a dialog, the old one doesn't need to be animated
1250             // away. This can happen if the app cancels and re-starts auth during configuration
1251             // change. This is ugly because we also have to do things on onConfigurationChanged
1252             // here.
1253             mCurrentDialog.dismissWithoutCallback(false /* animate */);
1254         }
1255 
1256         mReceiver = (IBiometricSysuiReceiver) args.arg2;
1257         for (Callback cb : mCallbacks) {
1258             cb.onBiometricPromptShown();
1259         }
1260         mCurrentDialog = newDialog;
1261 
1262         // TODO(b/339532378): We should check whether |allowBackgroundAuthentication| should be
1263         //  removed.
1264         if (!promptInfo.isAllowBackgroundAuthentication() && !isOwnerInForeground()) {
1265             cancelIfOwnerIsNotInForeground();
1266         } else {
1267             mCurrentDialog.show(mWindowManager);
1268         }
1269     }
1270 
onDialogDismissed(@ismissedReason int reason)1271     private void onDialogDismissed(@DismissedReason int reason) {
1272         if (DEBUG) Log.d(TAG, "onDialogDismissed: " + reason);
1273         if (mCurrentDialog == null) {
1274             Log.w(TAG, "Dialog already dismissed");
1275         }
1276 
1277         for (Callback cb : mCallbacks) {
1278             cb.onBiometricPromptDismissed();
1279         }
1280 
1281         mReceiver = null;
1282         mCurrentDialog = null;
1283     }
1284 
1285     @Override
onConfigChanged(Configuration newConfig)1286     public void onConfigChanged(Configuration newConfig) {
1287         updateSensorLocations();
1288 
1289         // TODO(b/287311775): consider removing this to retain the UI cleanly vs re-creating
1290         if (mCurrentDialog != null) {
1291             final PromptViewModel viewModel = mCurrentDialog.getViewModel();
1292             mCurrentDialog.dismissWithoutCallback(false /* animate */);
1293             mCurrentDialog = null;
1294             showDialog(mCurrentDialogArgs, true /* skipAnimation */, viewModel);
1295         }
1296     }
1297 
onOrientationChanged()1298     private void onOrientationChanged() {
1299         updateSensorLocations();
1300         if (mCurrentDialog != null) {
1301             mCurrentDialog.onOrientationChanged();
1302         }
1303     }
1304 
buildDialog(@ackground DelayableExecutor bgExecutor, PromptInfo promptInfo, boolean requireConfirmation, int userId, int[] sensorIds, String opPackageName, boolean skipIntro, long operationId, long requestId, @NonNull WakefulnessLifecycle wakefulnessLifecycle, @NonNull AuthDialogPanelInteractionDetector panelInteractionDetector, @NonNull UserManager userManager, @NonNull LockPatternUtils lockPatternUtils, @NonNull PromptViewModel viewModel)1305     protected AuthDialog buildDialog(@Background DelayableExecutor bgExecutor,
1306             PromptInfo promptInfo, boolean requireConfirmation, int userId, int[] sensorIds,
1307             String opPackageName, boolean skipIntro, long operationId, long requestId,
1308             @NonNull WakefulnessLifecycle wakefulnessLifecycle,
1309             @NonNull AuthDialogPanelInteractionDetector panelInteractionDetector,
1310             @NonNull UserManager userManager,
1311             @NonNull LockPatternUtils lockPatternUtils,
1312             @NonNull PromptViewModel viewModel) {
1313         final AuthContainerView.Config config = new AuthContainerView.Config();
1314         config.mContext = mContext;
1315         config.mCallback = this;
1316         config.mPromptInfo = promptInfo;
1317         config.mRequireConfirmation = requireConfirmation;
1318         config.mUserId = userId;
1319         config.mOpPackageName = opPackageName;
1320         config.mSkipIntro = skipIntro;
1321         config.mOperationId = operationId;
1322         config.mRequestId = requestId;
1323         config.mSensorIds = sensorIds;
1324         config.mScaleProvider = this::getScaleFactor;
1325         return new AuthContainerView(config, mApplicationCoroutineScope, mFpProps, mFaceProps,
1326                 wakefulnessLifecycle, panelInteractionDetector, userManager, lockPatternUtils,
1327                 mInteractionJankMonitor, mPromptSelectorInteractor, viewModel,
1328                 mCredentialViewModelProvider, bgExecutor, mVibratorHelper);
1329     }
1330 
1331     @Override
dump(@onNull PrintWriter pw, @NonNull String[] args)1332     public void dump(@NonNull PrintWriter pw, @NonNull String[] args) {
1333         final AuthDialog dialog = mCurrentDialog;
1334         pw.println("  mCachedDisplayInfo=" + mCachedDisplayInfo);
1335         pw.println("  mScaleFactor=" + mScaleFactor);
1336         pw.println("  fingerprintSensorLocationInNaturalOrientation="
1337                 + getFingerprintSensorLocationInNaturalOrientation());
1338         pw.println("  fingerprintSensorLocation=" + getFingerprintSensorLocation());
1339         pw.println("  udfpsBounds=" + mUdfpsBounds);
1340         pw.println("  allFingerprintAuthenticatorsRegistered="
1341                 + mAllFingerprintAuthenticatorsRegistered);
1342         pw.println("  currentDialog=" + dialog);
1343         if (dialog != null) {
1344             dialog.dump(pw, args);
1345         }
1346     }
1347 
1348     /**
1349      * Provides a float that represents the resolution scale(if the controller is for UDFPS).
1350      */
1351     public interface ScaleFactorProvider {
1352         /**
1353          * Returns a float representing the scaled resolution(if the controller if for UDFPS).
1354          */
provide()1355         float provide();
1356     }
1357 
1358     /**
1359      * AuthController callback used to receive signal for when biometric authenticators are
1360      * registered.
1361      */
1362     public interface Callback {
1363         /**
1364          * Called when authenticators are registered. If authenticators are already
1365          * registered before this call, this callback will never be triggered.
1366          */
onAllAuthenticatorsRegistered(@odality int modality)1367         default void onAllAuthenticatorsRegistered(@Modality int modality) {}
1368 
1369         /**
1370          * Called when enrollments have changed. This is called after boot and on changes to
1371          * enrollment.
1372          */
onEnrollmentsChanged(@odality int modality)1373         default void onEnrollmentsChanged(@Modality int modality) {}
1374 
1375         /**
1376          * Called when enrollments have changed. This is called after boot and on changes to
1377          * enrollment.
1378          */
onEnrollmentsChanged( @onNull BiometricType biometricType, int userId, boolean hasEnrollments )1379         default void onEnrollmentsChanged(
1380                 @NonNull BiometricType biometricType,
1381                 int userId,
1382                 boolean hasEnrollments
1383         ) {}
1384 
1385         /**
1386          * Called when the biometric prompt starts showing.
1387          */
onBiometricPromptShown()1388         default void onBiometricPromptShown() {}
1389 
1390         /**
1391          * Called when the biometric prompt is no longer showing.
1392          */
onBiometricPromptDismissed()1393         default void onBiometricPromptDismissed() {}
1394 
1395         /**
1396          * Called when the location of the fingerprint sensor changes. The location in pixels can
1397          * change due to resolution changes.
1398          */
onFingerprintLocationChanged()1399         default void onFingerprintLocationChanged() {}
1400 
1401         /**
1402          * Called when the location of the under display fingerprint sensor changes. The location in
1403          * pixels can change due to resolution changes.
1404          *
1405          * On devices with UDFPS, this is always called alongside
1406          * {@link #onFingerprintLocationChanged}.
1407          */
onUdfpsLocationChanged(UdfpsOverlayParams udfpsOverlayParams)1408         default void onUdfpsLocationChanged(UdfpsOverlayParams udfpsOverlayParams) {}
1409     }
1410 }
1411