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