1 /* 2 * Copyright (C) 2019 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 android.annotation.IntDef; 20 import android.annotation.NonNull; 21 import android.annotation.Nullable; 22 import android.content.Context; 23 import android.graphics.PixelFormat; 24 import android.hardware.biometrics.BiometricAuthenticator; 25 import android.hardware.biometrics.BiometricConstants; 26 import android.os.Binder; 27 import android.os.Bundle; 28 import android.os.Handler; 29 import android.os.IBinder; 30 import android.os.Looper; 31 import android.os.UserManager; 32 import android.util.Log; 33 import android.view.KeyEvent; 34 import android.view.LayoutInflater; 35 import android.view.View; 36 import android.view.ViewGroup; 37 import android.view.WindowInsets; 38 import android.view.WindowManager; 39 import android.view.animation.Interpolator; 40 import android.widget.FrameLayout; 41 import android.widget.ImageView; 42 import android.widget.LinearLayout; 43 import android.widget.ScrollView; 44 45 import com.android.internal.annotations.VisibleForTesting; 46 import com.android.systemui.Dependency; 47 import com.android.systemui.Interpolators; 48 import com.android.systemui.R; 49 import com.android.systemui.keyguard.WakefulnessLifecycle; 50 51 import java.lang.annotation.Retention; 52 import java.lang.annotation.RetentionPolicy; 53 54 /** 55 * Top level container/controller for the BiometricPrompt UI. 56 */ 57 public class AuthContainerView extends LinearLayout 58 implements AuthDialog, WakefulnessLifecycle.Observer { 59 60 private static final String TAG = "BiometricPrompt/AuthContainerView"; 61 private static final int ANIMATION_DURATION_SHOW_MS = 250; 62 private static final int ANIMATION_DURATION_AWAY_MS = 350; // ms 63 64 static final int STATE_UNKNOWN = 0; 65 static final int STATE_ANIMATING_IN = 1; 66 static final int STATE_PENDING_DISMISS = 2; 67 static final int STATE_SHOWING = 3; 68 static final int STATE_ANIMATING_OUT = 4; 69 static final int STATE_GONE = 5; 70 71 @Retention(RetentionPolicy.SOURCE) 72 @IntDef({STATE_UNKNOWN, STATE_ANIMATING_IN, STATE_PENDING_DISMISS, STATE_SHOWING, 73 STATE_ANIMATING_OUT, STATE_GONE}) 74 @interface ContainerState {} 75 76 final Config mConfig; 77 final int mEffectiveUserId; 78 private final Handler mHandler; 79 private final Injector mInjector; 80 private final IBinder mWindowToken = new Binder(); 81 private final WindowManager mWindowManager; 82 private final AuthPanelController mPanelController; 83 private final Interpolator mLinearOutSlowIn; 84 @VisibleForTesting final BiometricCallback mBiometricCallback; 85 private final CredentialCallback mCredentialCallback; 86 87 @VisibleForTesting final FrameLayout mFrameLayout; 88 @VisibleForTesting @Nullable AuthBiometricView mBiometricView; 89 @VisibleForTesting @Nullable AuthCredentialView mCredentialView; 90 91 @VisibleForTesting final ImageView mBackgroundView; 92 @VisibleForTesting final ScrollView mBiometricScrollView; 93 private final View mPanelView; 94 95 private final float mTranslationY; 96 97 @VisibleForTesting final WakefulnessLifecycle mWakefulnessLifecycle; 98 99 private @ContainerState int mContainerState = STATE_UNKNOWN; 100 101 // Non-null only if the dialog is in the act of dismissing and has not sent the reason yet. 102 @Nullable @AuthDialogCallback.DismissedReason Integer mPendingCallbackReason; 103 // HAT received from LockSettingsService when credential is verified. 104 @Nullable byte[] mCredentialAttestation; 105 106 static class Config { 107 Context mContext; 108 AuthDialogCallback mCallback; 109 Bundle mBiometricPromptBundle; 110 boolean mRequireConfirmation; 111 int mUserId; 112 String mOpPackageName; 113 int mModalityMask; 114 boolean mSkipIntro; 115 long mOperationId; 116 int mSysUiSessionId; 117 } 118 119 public static class Builder { 120 Config mConfig; 121 Builder(Context context)122 public Builder(Context context) { 123 mConfig = new Config(); 124 mConfig.mContext = context; 125 } 126 setCallback(AuthDialogCallback callback)127 public Builder setCallback(AuthDialogCallback callback) { 128 mConfig.mCallback = callback; 129 return this; 130 } 131 setBiometricPromptBundle(Bundle bundle)132 public Builder setBiometricPromptBundle(Bundle bundle) { 133 mConfig.mBiometricPromptBundle = bundle; 134 return this; 135 } 136 setRequireConfirmation(boolean requireConfirmation)137 public Builder setRequireConfirmation(boolean requireConfirmation) { 138 mConfig.mRequireConfirmation = requireConfirmation; 139 return this; 140 } 141 setUserId(int userId)142 public Builder setUserId(int userId) { 143 mConfig.mUserId = userId; 144 return this; 145 } 146 setOpPackageName(String opPackageName)147 public Builder setOpPackageName(String opPackageName) { 148 mConfig.mOpPackageName = opPackageName; 149 return this; 150 } 151 setSkipIntro(boolean skip)152 public Builder setSkipIntro(boolean skip) { 153 mConfig.mSkipIntro = skip; 154 return this; 155 } 156 setOperationId(long operationId)157 public Builder setOperationId(long operationId) { 158 mConfig.mOperationId = operationId; 159 return this; 160 } 161 setSysUiSessionId(int sysUiSessionId)162 public Builder setSysUiSessionId(int sysUiSessionId) { 163 mConfig.mSysUiSessionId = sysUiSessionId; 164 return this; 165 } 166 build(int modalityMask)167 public AuthContainerView build(int modalityMask) { 168 mConfig.mModalityMask = modalityMask; 169 return new AuthContainerView(mConfig, new Injector()); 170 } 171 } 172 173 public static class Injector { getBiometricScrollView(FrameLayout parent)174 ScrollView getBiometricScrollView(FrameLayout parent) { 175 return parent.findViewById(R.id.biometric_scrollview); 176 } 177 inflateContainerView(LayoutInflater factory, ViewGroup root)178 FrameLayout inflateContainerView(LayoutInflater factory, ViewGroup root) { 179 return (FrameLayout) factory.inflate( 180 R.layout.auth_container_view, root, false /* attachToRoot */); 181 } 182 getPanelController(Context context, View panelView)183 AuthPanelController getPanelController(Context context, View panelView) { 184 return new AuthPanelController(context, panelView); 185 } 186 getBackgroundView(FrameLayout parent)187 ImageView getBackgroundView(FrameLayout parent) { 188 return parent.findViewById(R.id.background); 189 } 190 getPanelView(FrameLayout parent)191 View getPanelView(FrameLayout parent) { 192 return parent.findViewById(R.id.panel); 193 } 194 getAnimateCredentialStartDelayMs()195 int getAnimateCredentialStartDelayMs() { 196 return AuthDialog.ANIMATE_CREDENTIAL_START_DELAY_MS; 197 } 198 getUserManager(Context context)199 UserManager getUserManager(Context context) { 200 return UserManager.get(context); 201 } 202 getCredentialType(Context context, int effectiveUserId)203 int getCredentialType(Context context, int effectiveUserId) { 204 return Utils.getCredentialType(context, effectiveUserId); 205 } 206 } 207 208 @VisibleForTesting 209 final class BiometricCallback implements AuthBiometricView.Callback { 210 @Override onAction(int action)211 public void onAction(int action) { 212 Log.d(TAG, "onAction: " + action 213 + ", sysUiSessionId: " + mConfig.mSysUiSessionId 214 + ", state: " + mContainerState); 215 switch (action) { 216 case AuthBiometricView.Callback.ACTION_AUTHENTICATED: 217 animateAway(AuthDialogCallback.DISMISSED_BIOMETRIC_AUTHENTICATED); 218 break; 219 case AuthBiometricView.Callback.ACTION_USER_CANCELED: 220 sendEarlyUserCanceled(); 221 animateAway(AuthDialogCallback.DISMISSED_USER_CANCELED); 222 break; 223 case AuthBiometricView.Callback.ACTION_BUTTON_NEGATIVE: 224 animateAway(AuthDialogCallback.DISMISSED_BUTTON_NEGATIVE); 225 break; 226 case AuthBiometricView.Callback.ACTION_BUTTON_TRY_AGAIN: 227 mConfig.mCallback.onTryAgainPressed(); 228 break; 229 case AuthBiometricView.Callback.ACTION_ERROR: 230 animateAway(AuthDialogCallback.DISMISSED_ERROR); 231 break; 232 case AuthBiometricView.Callback.ACTION_USE_DEVICE_CREDENTIAL: 233 mConfig.mCallback.onDeviceCredentialPressed(); 234 mHandler.postDelayed(() -> { 235 addCredentialView(false /* animatePanel */, true /* animateContents */); 236 }, mInjector.getAnimateCredentialStartDelayMs()); 237 break; 238 default: 239 Log.e(TAG, "Unhandled action: " + action); 240 } 241 } 242 } 243 244 final class CredentialCallback implements AuthCredentialView.Callback { 245 @Override onCredentialMatched(byte[] attestation)246 public void onCredentialMatched(byte[] attestation) { 247 mCredentialAttestation = attestation; 248 animateAway(AuthDialogCallback.DISMISSED_CREDENTIAL_AUTHENTICATED); 249 } 250 } 251 252 @VisibleForTesting AuthContainerView(Config config, Injector injector)253 AuthContainerView(Config config, Injector injector) { 254 super(config.mContext); 255 256 mConfig = config; 257 mInjector = injector; 258 259 mEffectiveUserId = mInjector.getUserManager(mContext) 260 .getCredentialOwnerProfile(mConfig.mUserId); 261 262 mHandler = new Handler(Looper.getMainLooper()); 263 mWindowManager = mContext.getSystemService(WindowManager.class); 264 mWakefulnessLifecycle = Dependency.get(WakefulnessLifecycle.class); 265 266 mTranslationY = getResources() 267 .getDimension(R.dimen.biometric_dialog_animation_translation_offset); 268 mLinearOutSlowIn = Interpolators.LINEAR_OUT_SLOW_IN; 269 mBiometricCallback = new BiometricCallback(); 270 mCredentialCallback = new CredentialCallback(); 271 272 final LayoutInflater factory = LayoutInflater.from(mContext); 273 mFrameLayout = mInjector.inflateContainerView(factory, this); 274 275 mPanelView = mInjector.getPanelView(mFrameLayout); 276 mPanelController = mInjector.getPanelController(mContext, mPanelView); 277 278 // Inflate biometric view only if necessary. 279 if (Utils.isBiometricAllowed(mConfig.mBiometricPromptBundle)) { 280 if (config.mModalityMask == BiometricAuthenticator.TYPE_FINGERPRINT) { 281 mBiometricView = (AuthBiometricFingerprintView) 282 factory.inflate(R.layout.auth_biometric_fingerprint_view, null, false); 283 } else if (config.mModalityMask == BiometricAuthenticator.TYPE_FACE) { 284 mBiometricView = (AuthBiometricFaceView) 285 factory.inflate(R.layout.auth_biometric_face_view, null, false); 286 } else { 287 Log.e(TAG, "Unsupported biometric modality: " + config.mModalityMask); 288 mBiometricView = null; 289 mBackgroundView = null; 290 mBiometricScrollView = null; 291 return; 292 } 293 } 294 295 mBiometricScrollView = mInjector.getBiometricScrollView(mFrameLayout); 296 mBackgroundView = mInjector.getBackgroundView(mFrameLayout); 297 298 addView(mFrameLayout); 299 300 // TODO: De-dupe the logic with AuthCredentialPasswordView 301 setOnKeyListener((v, keyCode, event) -> { 302 if (keyCode != KeyEvent.KEYCODE_BACK) { 303 return false; 304 } 305 if (event.getAction() == KeyEvent.ACTION_UP) { 306 sendEarlyUserCanceled(); 307 animateAway(AuthDialogCallback.DISMISSED_USER_CANCELED); 308 } 309 return true; 310 }); 311 312 setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO); 313 setFocusableInTouchMode(true); 314 requestFocus(); 315 } 316 sendEarlyUserCanceled()317 void sendEarlyUserCanceled() { 318 mConfig.mCallback.onSystemEvent( 319 BiometricConstants.BIOMETRIC_SYSTEM_EVENT_EARLY_USER_CANCEL); 320 } 321 322 @Override isAllowDeviceCredentials()323 public boolean isAllowDeviceCredentials() { 324 return Utils.isDeviceCredentialAllowed(mConfig.mBiometricPromptBundle); 325 } 326 addBiometricView()327 private void addBiometricView() { 328 mBiometricView.setRequireConfirmation(mConfig.mRequireConfirmation); 329 mBiometricView.setPanelController(mPanelController); 330 mBiometricView.setBiometricPromptBundle(mConfig.mBiometricPromptBundle); 331 mBiometricView.setCallback(mBiometricCallback); 332 mBiometricView.setBackgroundView(mBackgroundView); 333 mBiometricView.setUserId(mConfig.mUserId); 334 mBiometricView.setEffectiveUserId(mEffectiveUserId); 335 mBiometricScrollView.addView(mBiometricView); 336 } 337 338 /** 339 * Adds the credential view. When going from biometric to credential view, the biometric 340 * view starts the panel expansion animation. If the credential view is being shown first, 341 * it should own the panel expansion. 342 * @param animatePanel if the credential view needs to own the panel expansion animation 343 */ addCredentialView(boolean animatePanel, boolean animateContents)344 private void addCredentialView(boolean animatePanel, boolean animateContents) { 345 final LayoutInflater factory = LayoutInflater.from(mContext); 346 347 final @Utils.CredentialType int credentialType = mInjector.getCredentialType( 348 mContext, mEffectiveUserId); 349 350 switch (credentialType) { 351 case Utils.CREDENTIAL_PATTERN: 352 mCredentialView = (AuthCredentialView) factory.inflate( 353 R.layout.auth_credential_pattern_view, null, false); 354 break; 355 case Utils.CREDENTIAL_PIN: 356 case Utils.CREDENTIAL_PASSWORD: 357 mCredentialView = (AuthCredentialView) factory.inflate( 358 R.layout.auth_credential_password_view, null, false); 359 break; 360 default: 361 throw new IllegalStateException("Unknown credential type: " + credentialType); 362 } 363 364 // The background is used for detecting taps / cancelling authentication. Since the 365 // credential view is full-screen and should not be canceled from background taps, 366 // disable it. 367 mBackgroundView.setOnClickListener(null); 368 mBackgroundView.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO); 369 370 mCredentialView.setContainerView(this); 371 mCredentialView.setUserId(mConfig.mUserId); 372 mCredentialView.setOperationId(mConfig.mOperationId); 373 mCredentialView.setEffectiveUserId(mEffectiveUserId); 374 mCredentialView.setCredentialType(credentialType); 375 mCredentialView.setCallback(mCredentialCallback); 376 mCredentialView.setBiometricPromptBundle(mConfig.mBiometricPromptBundle); 377 mCredentialView.setPanelController(mPanelController, animatePanel); 378 mCredentialView.setShouldAnimateContents(animateContents); 379 mFrameLayout.addView(mCredentialView); 380 } 381 382 @Override onMeasure(int widthMeasureSpec, int heightMeasureSpec)383 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 384 super.onMeasure(widthMeasureSpec, heightMeasureSpec); 385 mPanelController.setContainerDimensions(getMeasuredWidth(), getMeasuredHeight()); 386 } 387 388 @Override onAttachedToWindow()389 public void onAttachedToWindow() { 390 super.onAttachedToWindow(); 391 onAttachedToWindowInternal(); 392 } 393 394 @VisibleForTesting onAttachedToWindowInternal()395 void onAttachedToWindowInternal() { 396 mWakefulnessLifecycle.addObserver(this); 397 398 if (Utils.isBiometricAllowed(mConfig.mBiometricPromptBundle)) { 399 addBiometricView(); 400 } else if (Utils.isDeviceCredentialAllowed(mConfig.mBiometricPromptBundle)) { 401 addCredentialView(true /* animatePanel */, false /* animateContents */); 402 } else { 403 throw new IllegalStateException("Unknown configuration: " 404 + Utils.getAuthenticators(mConfig.mBiometricPromptBundle)); 405 } 406 407 if (mConfig.mSkipIntro) { 408 mContainerState = STATE_SHOWING; 409 } else { 410 mContainerState = STATE_ANIMATING_IN; 411 // The background panel and content are different views since we need to be able to 412 // animate them separately in other places. 413 mPanelView.setY(mTranslationY); 414 mBiometricScrollView.setY(mTranslationY); 415 416 setAlpha(0f); 417 postOnAnimation(() -> { 418 mPanelView.animate() 419 .translationY(0) 420 .setDuration(ANIMATION_DURATION_SHOW_MS) 421 .setInterpolator(mLinearOutSlowIn) 422 .withLayer() 423 .withEndAction(this::onDialogAnimatedIn) 424 .start(); 425 mBiometricScrollView.animate() 426 .translationY(0) 427 .setDuration(ANIMATION_DURATION_SHOW_MS) 428 .setInterpolator(mLinearOutSlowIn) 429 .withLayer() 430 .start(); 431 if (mCredentialView != null && mCredentialView.isAttachedToWindow()) { 432 mCredentialView.setY(mTranslationY); 433 mCredentialView.animate() 434 .translationY(0) 435 .setDuration(ANIMATION_DURATION_SHOW_MS) 436 .setInterpolator(mLinearOutSlowIn) 437 .withLayer() 438 .start(); 439 } 440 animate() 441 .alpha(1f) 442 .setDuration(ANIMATION_DURATION_SHOW_MS) 443 .setInterpolator(mLinearOutSlowIn) 444 .withLayer() 445 .start(); 446 }); 447 } 448 } 449 450 @Override onDetachedFromWindow()451 public void onDetachedFromWindow() { 452 super.onDetachedFromWindow(); 453 mWakefulnessLifecycle.removeObserver(this); 454 } 455 456 @Override onStartedGoingToSleep()457 public void onStartedGoingToSleep() { 458 animateAway(AuthDialogCallback.DISMISSED_USER_CANCELED); 459 } 460 461 @Override show(WindowManager wm, @Nullable Bundle savedState)462 public void show(WindowManager wm, @Nullable Bundle savedState) { 463 if (mBiometricView != null) { 464 mBiometricView.restoreState(savedState); 465 } 466 wm.addView(this, getLayoutParams(mWindowToken)); 467 } 468 469 @Override dismissWithoutCallback(boolean animate)470 public void dismissWithoutCallback(boolean animate) { 471 if (animate) { 472 animateAway(false /* sendReason */, 0 /* reason */); 473 } else { 474 removeWindowIfAttached(false /* sendReason */); 475 } 476 } 477 478 @Override dismissFromSystemServer()479 public void dismissFromSystemServer() { 480 removeWindowIfAttached(true /* sendReason */); 481 } 482 483 @Override onAuthenticationSucceeded()484 public void onAuthenticationSucceeded() { 485 mBiometricView.onAuthenticationSucceeded(); 486 } 487 488 @Override onAuthenticationFailed(String failureReason)489 public void onAuthenticationFailed(String failureReason) { 490 mBiometricView.onAuthenticationFailed(failureReason); 491 } 492 493 @Override onHelp(String help)494 public void onHelp(String help) { 495 mBiometricView.onHelp(help); 496 } 497 498 @Override onError(String error)499 public void onError(String error) { 500 mBiometricView.onError(error); 501 } 502 503 @Override onSaveState(@onNull Bundle outState)504 public void onSaveState(@NonNull Bundle outState) { 505 outState.putInt(AuthDialog.KEY_CONTAINER_STATE, mContainerState); 506 // In the case where biometric and credential are both allowed, we can assume that 507 // biometric isn't showing if credential is showing since biometric is shown first. 508 outState.putBoolean(AuthDialog.KEY_BIOMETRIC_SHOWING, 509 mBiometricView != null && mCredentialView == null); 510 outState.putBoolean(AuthDialog.KEY_CREDENTIAL_SHOWING, mCredentialView != null); 511 512 if (mBiometricView != null) { 513 mBiometricView.onSaveState(outState); 514 } 515 } 516 517 @Override getOpPackageName()518 public String getOpPackageName() { 519 return mConfig.mOpPackageName; 520 } 521 522 @Override animateToCredentialUI()523 public void animateToCredentialUI() { 524 mBiometricView.startTransitionToCredentialUI(); 525 } 526 527 @VisibleForTesting animateAway(int reason)528 void animateAway(int reason) { 529 animateAway(true /* sendReason */, reason); 530 } 531 animateAway(boolean sendReason, @AuthDialogCallback.DismissedReason int reason)532 private void animateAway(boolean sendReason, @AuthDialogCallback.DismissedReason int reason) { 533 if (mContainerState == STATE_ANIMATING_IN) { 534 Log.w(TAG, "startDismiss(): waiting for onDialogAnimatedIn"); 535 mContainerState = STATE_PENDING_DISMISS; 536 return; 537 } 538 539 if (mContainerState == STATE_ANIMATING_OUT) { 540 Log.w(TAG, "Already dismissing, sendReason: " + sendReason + " reason: " + reason); 541 return; 542 } 543 mContainerState = STATE_ANIMATING_OUT; 544 545 if (sendReason) { 546 mPendingCallbackReason = reason; 547 } else { 548 mPendingCallbackReason = null; 549 } 550 551 final Runnable endActionRunnable = () -> { 552 setVisibility(View.INVISIBLE); 553 removeWindowIfAttached(true /* sendReason */); 554 }; 555 556 postOnAnimation(() -> { 557 mPanelView.animate() 558 .translationY(mTranslationY) 559 .setDuration(ANIMATION_DURATION_AWAY_MS) 560 .setInterpolator(mLinearOutSlowIn) 561 .withLayer() 562 .withEndAction(endActionRunnable) 563 .start(); 564 mBiometricScrollView.animate() 565 .translationY(mTranslationY) 566 .setDuration(ANIMATION_DURATION_AWAY_MS) 567 .setInterpolator(mLinearOutSlowIn) 568 .withLayer() 569 .start(); 570 if (mCredentialView != null && mCredentialView.isAttachedToWindow()) { 571 mCredentialView.animate() 572 .translationY(mTranslationY) 573 .setDuration(ANIMATION_DURATION_AWAY_MS) 574 .setInterpolator(mLinearOutSlowIn) 575 .withLayer() 576 .start(); 577 } 578 animate() 579 .alpha(0f) 580 .setDuration(ANIMATION_DURATION_AWAY_MS) 581 .setInterpolator(mLinearOutSlowIn) 582 .withLayer() 583 .start(); 584 }); 585 } 586 sendPendingCallbackIfNotNull()587 private void sendPendingCallbackIfNotNull() { 588 Log.d(TAG, "pendingCallback: " + mPendingCallbackReason 589 + " sysUISessionId: " + mConfig.mSysUiSessionId); 590 if (mPendingCallbackReason != null) { 591 mConfig.mCallback.onDismissed(mPendingCallbackReason, mCredentialAttestation); 592 mPendingCallbackReason = null; 593 } 594 } 595 removeWindowIfAttached(boolean sendReason)596 private void removeWindowIfAttached(boolean sendReason) { 597 if (sendReason) { 598 sendPendingCallbackIfNotNull(); 599 } 600 601 if (mContainerState == STATE_GONE) { 602 Log.w(TAG, "Container already STATE_GONE, mSysUiSessionId: " + mConfig.mSysUiSessionId); 603 return; 604 } 605 Log.d(TAG, "Removing container, mSysUiSessionId: " + mConfig.mSysUiSessionId); 606 mContainerState = STATE_GONE; 607 mWindowManager.removeView(this); 608 } 609 onDialogAnimatedIn()610 private void onDialogAnimatedIn() { 611 if (mContainerState == STATE_PENDING_DISMISS) { 612 Log.d(TAG, "onDialogAnimatedIn(): mPendingDismissDialog=true, dismissing now"); 613 animateAway(false /* sendReason */, 0); 614 return; 615 } 616 mContainerState = STATE_SHOWING; 617 if (mBiometricView != null) { 618 mBiometricView.onDialogAnimatedIn(); 619 } 620 } 621 622 /** 623 * @param windowToken token for the window 624 * @return 625 */ getLayoutParams(IBinder windowToken)626 public static WindowManager.LayoutParams getLayoutParams(IBinder windowToken) { 627 final int windowFlags = WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED 628 | WindowManager.LayoutParams.FLAG_SECURE; 629 final WindowManager.LayoutParams lp = new WindowManager.LayoutParams( 630 ViewGroup.LayoutParams.MATCH_PARENT, 631 ViewGroup.LayoutParams.MATCH_PARENT, 632 WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL, 633 windowFlags, 634 PixelFormat.TRANSLUCENT); 635 lp.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS; 636 lp.setFitInsetsTypes(lp.getFitInsetsTypes() & ~WindowInsets.Type.ime()); 637 lp.setTitle("BiometricPrompt"); 638 lp.token = windowToken; 639 return lp; 640 } 641 } 642