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