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