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