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