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