1 /*
2  * Copyright (C) 2014 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 package com.android.keyguard;
17 
18 import android.app.Activity;
19 import android.app.AlertDialog;
20 import android.app.admin.DevicePolicyManager;
21 import android.content.Context;
22 import android.content.res.ColorStateList;
23 import android.graphics.Rect;
24 import android.metrics.LogMaker;
25 import android.os.UserHandle;
26 import android.util.AttributeSet;
27 import android.util.Log;
28 import android.util.Slog;
29 import android.util.StatsLog;
30 import android.util.TypedValue;
31 import android.view.LayoutInflater;
32 import android.view.MotionEvent;
33 import android.view.VelocityTracker;
34 import android.view.View;
35 import android.view.ViewConfiguration;
36 import android.view.WindowManager;
37 import android.widget.FrameLayout;
38 
39 import androidx.annotation.VisibleForTesting;
40 import androidx.dynamicanimation.animation.DynamicAnimation;
41 import androidx.dynamicanimation.animation.SpringAnimation;
42 
43 import com.android.internal.logging.MetricsLogger;
44 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
45 import com.android.internal.widget.LockPatternUtils;
46 import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
47 import com.android.settingslib.utils.ThreadUtils;
48 import com.android.systemui.Dependency;
49 import com.android.systemui.R;
50 import com.android.systemui.SystemUIFactory;
51 import com.android.systemui.statusbar.phone.UnlockMethodCache;
52 import com.android.systemui.util.InjectionInflationController;
53 
54 public class KeyguardSecurityContainer extends FrameLayout implements KeyguardSecurityView {
55     private static final boolean DEBUG = KeyguardConstants.DEBUG;
56     private static final String TAG = "KeyguardSecurityView";
57 
58     private static final int USER_TYPE_PRIMARY = 1;
59     private static final int USER_TYPE_WORK_PROFILE = 2;
60     private static final int USER_TYPE_SECONDARY_USER = 3;
61 
62     // Bouncer is dismissed due to no security.
63     private static final int BOUNCER_DISMISS_NONE_SECURITY = 0;
64     // Bouncer is dismissed due to pin, password or pattern entered.
65     private static final int BOUNCER_DISMISS_PASSWORD = 1;
66     // Bouncer is dismissed due to biometric (face, fingerprint or iris) authenticated.
67     private static final int BOUNCER_DISMISS_BIOMETRIC = 2;
68     // Bouncer is dismissed due to extended access granted.
69     private static final int BOUNCER_DISMISS_EXTENDED_ACCESS = 3;
70     // Bouncer is dismissed due to sim card unlock code entered.
71     private static final int BOUNCER_DISMISS_SIM = 4;
72 
73     // Make the view move slower than the finger, as if the spring were applying force.
74     private static final float TOUCH_Y_MULTIPLIER = 0.25f;
75     // How much you need to drag the bouncer to trigger an auth retry (in dps.)
76     private static final float MIN_DRAG_SIZE = 10;
77     // How much to scale the default slop by, to avoid accidental drags.
78     private static final float SLOP_SCALE = 2f;
79 
80     private KeyguardSecurityModel mSecurityModel;
81     private LockPatternUtils mLockPatternUtils;
82 
83     private KeyguardSecurityViewFlipper mSecurityViewFlipper;
84     private boolean mIsVerifyUnlockOnly;
85     private SecurityMode mCurrentSecuritySelection = SecurityMode.Invalid;
86     private KeyguardSecurityView mCurrentSecurityView;
87     private SecurityCallback mSecurityCallback;
88     private AlertDialog mAlertDialog;
89     private InjectionInflationController mInjectionInflationController;
90     private boolean mSwipeUpToRetry;
91 
92     private final ViewConfiguration mViewConfiguration;
93     private final SpringAnimation mSpringAnimation;
94     private final VelocityTracker mVelocityTracker = VelocityTracker.obtain();
95     private final KeyguardUpdateMonitor mUpdateMonitor;
96     private final UnlockMethodCache mUnlockMethodCache;
97 
98     private final MetricsLogger mMetricsLogger = Dependency.get(MetricsLogger.class);
99     private float mLastTouchY = -1;
100     private int mActivePointerId = -1;
101     private boolean mIsDragging;
102     private float mStartTouchY = -1;
103 
104     // Used to notify the container when something interesting happens.
105     public interface SecurityCallback {
dismiss(boolean authenticated, int targetUserId)106         public boolean dismiss(boolean authenticated, int targetUserId);
userActivity()107         public void userActivity();
onSecurityModeChanged(SecurityMode securityMode, boolean needsInput)108         public void onSecurityModeChanged(SecurityMode securityMode, boolean needsInput);
109 
110         /**
111          * @param strongAuth wheher the user has authenticated with strong authentication like
112          *                   pattern, password or PIN but not by trust agents or fingerprint
113          * @param targetUserId a user that needs to be the foreground user at the finish completion.
114          */
finish(boolean strongAuth, int targetUserId)115         public void finish(boolean strongAuth, int targetUserId);
reset()116         public void reset();
onCancelClicked()117         public void onCancelClicked();
118     }
119 
KeyguardSecurityContainer(Context context, AttributeSet attrs)120     public KeyguardSecurityContainer(Context context, AttributeSet attrs) {
121         this(context, attrs, 0);
122     }
123 
KeyguardSecurityContainer(Context context)124     public KeyguardSecurityContainer(Context context) {
125         this(context, null, 0);
126     }
127 
KeyguardSecurityContainer(Context context, AttributeSet attrs, int defStyle)128     public KeyguardSecurityContainer(Context context, AttributeSet attrs, int defStyle) {
129         super(context, attrs, defStyle);
130         mSecurityModel = new KeyguardSecurityModel(context);
131         mLockPatternUtils = new LockPatternUtils(context);
132         mUpdateMonitor = KeyguardUpdateMonitor.getInstance(mContext);
133         mSpringAnimation = new SpringAnimation(this, DynamicAnimation.Y);
134         mInjectionInflationController =  new InjectionInflationController(
135             SystemUIFactory.getInstance().getRootComponent());
136         mUnlockMethodCache = UnlockMethodCache.getInstance(context);
137         mViewConfiguration = ViewConfiguration.get(context);
138     }
139 
setSecurityCallback(SecurityCallback callback)140     public void setSecurityCallback(SecurityCallback callback) {
141         mSecurityCallback = callback;
142     }
143 
144     @Override
onResume(int reason)145     public void onResume(int reason) {
146         if (mCurrentSecuritySelection != SecurityMode.None) {
147             getSecurityView(mCurrentSecuritySelection).onResume(reason);
148         }
149         updateBiometricRetry();
150     }
151 
152     @Override
onPause()153     public void onPause() {
154         if (mAlertDialog != null) {
155             mAlertDialog.dismiss();
156             mAlertDialog = null;
157         }
158         if (mCurrentSecuritySelection != SecurityMode.None) {
159             getSecurityView(mCurrentSecuritySelection).onPause();
160         }
161     }
162 
163     @Override
shouldDelayChildPressedState()164     public boolean shouldDelayChildPressedState() {
165         return true;
166     }
167 
168     @Override
onInterceptTouchEvent(MotionEvent event)169     public boolean onInterceptTouchEvent(MotionEvent event) {
170         switch (event.getActionMasked()) {
171             case MotionEvent.ACTION_DOWN:
172                 int pointerIndex = event.getActionIndex();
173                 mStartTouchY = event.getY(pointerIndex);
174                 mActivePointerId = event.getPointerId(pointerIndex);
175                 mVelocityTracker.clear();
176                 break;
177             case MotionEvent.ACTION_MOVE:
178                 if (mIsDragging) {
179                     return true;
180                 }
181                 if (!mSwipeUpToRetry) {
182                     return false;
183                 }
184                 // Avoid dragging the pattern view
185                 if (mCurrentSecurityView.disallowInterceptTouch(event)) {
186                     return false;
187                 }
188                 int index = event.findPointerIndex(mActivePointerId);
189                 float touchSlop = mViewConfiguration.getScaledTouchSlop() * SLOP_SCALE;
190                 if (mCurrentSecurityView != null && index != -1
191                         && mStartTouchY - event.getY(index) > touchSlop) {
192                     mIsDragging = true;
193                     return true;
194                 }
195                 break;
196             case MotionEvent.ACTION_CANCEL:
197             case MotionEvent.ACTION_UP:
198                 mIsDragging = false;
199                 break;
200         }
201         return false;
202     }
203 
204     @Override
onTouchEvent(MotionEvent event)205     public boolean onTouchEvent(MotionEvent event) {
206         final int action = event.getActionMasked();
207         switch (action) {
208             case MotionEvent.ACTION_MOVE:
209                 mVelocityTracker.addMovement(event);
210                 int pointerIndex = event.findPointerIndex(mActivePointerId);
211                 float y = event.getY(pointerIndex);
212                 if (mLastTouchY != -1) {
213                     float dy = y - mLastTouchY;
214                     setTranslationY(getTranslationY() + dy * TOUCH_Y_MULTIPLIER);
215                 }
216                 mLastTouchY = y;
217                 break;
218             case MotionEvent.ACTION_UP:
219             case MotionEvent.ACTION_CANCEL:
220                 mActivePointerId = -1;
221                 mLastTouchY = -1;
222                 mIsDragging = false;
223                 startSpringAnimation(mVelocityTracker.getYVelocity());
224                 break;
225             case MotionEvent.ACTION_POINTER_UP:
226                 int index = event.getActionIndex();
227                 int pointerId = event.getPointerId(index);
228                 if (pointerId == mActivePointerId) {
229                     // This was our active pointer going up. Choose a new
230                     // active pointer and adjust accordingly.
231                     final int newPointerIndex = index == 0 ? 1 : 0;
232                     mLastTouchY = event.getY(newPointerIndex);
233                     mActivePointerId = event.getPointerId(newPointerIndex);
234                 }
235                 break;
236         }
237         if (action == MotionEvent.ACTION_UP) {
238             if (-getTranslationY() > TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
239                     MIN_DRAG_SIZE, getResources().getDisplayMetrics())
240                     && !mUpdateMonitor.isFaceDetectionRunning()) {
241                 mUpdateMonitor.requestFaceAuth();
242                 mCallback.userActivity();
243                 showMessage(null, null);
244             }
245         }
246         return true;
247     }
248 
startSpringAnimation(float startVelocity)249     private void startSpringAnimation(float startVelocity) {
250         mSpringAnimation
251             .setStartVelocity(startVelocity)
252             .animateToFinalPosition(0);
253     }
254 
startAppearAnimation()255     public void startAppearAnimation() {
256         if (mCurrentSecuritySelection != SecurityMode.None) {
257             getSecurityView(mCurrentSecuritySelection).startAppearAnimation();
258         }
259     }
260 
startDisappearAnimation(Runnable onFinishRunnable)261     public boolean startDisappearAnimation(Runnable onFinishRunnable) {
262         if (mCurrentSecuritySelection != SecurityMode.None) {
263             return getSecurityView(mCurrentSecuritySelection).startDisappearAnimation(
264                     onFinishRunnable);
265         }
266         return false;
267     }
268 
269     /**
270      * Enables/disables swipe up to retry on the bouncer.
271      */
updateBiometricRetry()272     private void updateBiometricRetry() {
273         SecurityMode securityMode = getSecurityMode();
274         mSwipeUpToRetry = mUnlockMethodCache.isFaceAuthEnabled()
275                 && securityMode != SecurityMode.SimPin
276                 && securityMode != SecurityMode.SimPuk
277                 && securityMode != SecurityMode.None;
278     }
279 
getTitle()280     public CharSequence getTitle() {
281         return mSecurityViewFlipper.getTitle();
282     }
283 
getSecurityView(SecurityMode securityMode)284     private KeyguardSecurityView getSecurityView(SecurityMode securityMode) {
285         final int securityViewIdForMode = getSecurityViewIdForMode(securityMode);
286         KeyguardSecurityView view = null;
287         final int children = mSecurityViewFlipper.getChildCount();
288         for (int child = 0; child < children; child++) {
289             if (mSecurityViewFlipper.getChildAt(child).getId() == securityViewIdForMode) {
290                 view = ((KeyguardSecurityView)mSecurityViewFlipper.getChildAt(child));
291                 break;
292             }
293         }
294         int layoutId = getLayoutIdFor(securityMode);
295         if (view == null && layoutId != 0) {
296             final LayoutInflater inflater = LayoutInflater.from(mContext);
297             if (DEBUG) Log.v(TAG, "inflating id = " + layoutId);
298             View v = mInjectionInflationController.injectable(inflater)
299                     .inflate(layoutId, mSecurityViewFlipper, false);
300             mSecurityViewFlipper.addView(v);
301             updateSecurityView(v);
302             view = (KeyguardSecurityView)v;
303             view.reset();
304         }
305 
306         return view;
307     }
308 
updateSecurityView(View view)309     private void updateSecurityView(View view) {
310         if (view instanceof KeyguardSecurityView) {
311             KeyguardSecurityView ksv = (KeyguardSecurityView) view;
312             ksv.setKeyguardCallback(mCallback);
313             ksv.setLockPatternUtils(mLockPatternUtils);
314         } else {
315             Log.w(TAG, "View " + view + " is not a KeyguardSecurityView");
316         }
317     }
318 
onFinishInflate()319     protected void onFinishInflate() {
320         mSecurityViewFlipper = findViewById(R.id.view_flipper);
321         mSecurityViewFlipper.setLockPatternUtils(mLockPatternUtils);
322     }
323 
setLockPatternUtils(LockPatternUtils utils)324     public void setLockPatternUtils(LockPatternUtils utils) {
325         mLockPatternUtils = utils;
326         mSecurityModel.setLockPatternUtils(utils);
327         mSecurityViewFlipper.setLockPatternUtils(mLockPatternUtils);
328     }
329 
330     @Override
fitSystemWindows(Rect insets)331     protected boolean fitSystemWindows(Rect insets) {
332         // Consume bottom insets because we're setting the padding locally (for IME and navbar.)
333         setPadding(getPaddingLeft(), getPaddingTop(), getPaddingRight(), insets.bottom);
334         insets.bottom = 0;
335         return false;
336     }
337 
showDialog(String title, String message)338     private void showDialog(String title, String message) {
339         if (mAlertDialog != null) {
340             mAlertDialog.dismiss();
341         }
342 
343         mAlertDialog = new AlertDialog.Builder(mContext)
344             .setTitle(title)
345             .setMessage(message)
346             .setCancelable(false)
347             .setNeutralButton(R.string.ok, null)
348             .create();
349         if (!(mContext instanceof Activity)) {
350             mAlertDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
351         }
352         mAlertDialog.show();
353     }
354 
showTimeoutDialog(int userId, int timeoutMs)355     private void showTimeoutDialog(int userId, int timeoutMs) {
356         int timeoutInSeconds = (int) timeoutMs / 1000;
357         int messageId = 0;
358 
359         switch (mSecurityModel.getSecurityMode(userId)) {
360             case Pattern:
361                 messageId = R.string.kg_too_many_failed_pattern_attempts_dialog_message;
362                 break;
363             case PIN:
364                 messageId = R.string.kg_too_many_failed_pin_attempts_dialog_message;
365                 break;
366             case Password:
367                 messageId = R.string.kg_too_many_failed_password_attempts_dialog_message;
368                 break;
369             // These don't have timeout dialogs.
370             case Invalid:
371             case None:
372             case SimPin:
373             case SimPuk:
374                 break;
375         }
376 
377         if (messageId != 0) {
378             final String message = mContext.getString(messageId,
379                     mLockPatternUtils.getCurrentFailedPasswordAttempts(userId),
380                     timeoutInSeconds);
381             showDialog(null, message);
382         }
383     }
384 
showAlmostAtWipeDialog(int attempts, int remaining, int userType)385     private void showAlmostAtWipeDialog(int attempts, int remaining, int userType) {
386         String message = null;
387         switch (userType) {
388             case USER_TYPE_PRIMARY:
389                 message = mContext.getString(R.string.kg_failed_attempts_almost_at_wipe,
390                         attempts, remaining);
391                 break;
392             case USER_TYPE_SECONDARY_USER:
393                 message = mContext.getString(R.string.kg_failed_attempts_almost_at_erase_user,
394                         attempts, remaining);
395                 break;
396             case USER_TYPE_WORK_PROFILE:
397                 message = mContext.getString(R.string.kg_failed_attempts_almost_at_erase_profile,
398                         attempts, remaining);
399                 break;
400         }
401         showDialog(null, message);
402     }
403 
showWipeDialog(int attempts, int userType)404     private void showWipeDialog(int attempts, int userType) {
405         String message = null;
406         switch (userType) {
407             case USER_TYPE_PRIMARY:
408                 message = mContext.getString(R.string.kg_failed_attempts_now_wiping,
409                         attempts);
410                 break;
411             case USER_TYPE_SECONDARY_USER:
412                 message = mContext.getString(R.string.kg_failed_attempts_now_erasing_user,
413                         attempts);
414                 break;
415             case USER_TYPE_WORK_PROFILE:
416                 message = mContext.getString(R.string.kg_failed_attempts_now_erasing_profile,
417                         attempts);
418                 break;
419         }
420         showDialog(null, message);
421     }
422 
reportFailedUnlockAttempt(int userId, int timeoutMs)423     private void reportFailedUnlockAttempt(int userId, int timeoutMs) {
424         // +1 for this time
425         final int failedAttempts = mLockPatternUtils.getCurrentFailedPasswordAttempts(userId) + 1;
426 
427         if (DEBUG) Log.d(TAG, "reportFailedPatternAttempt: #" + failedAttempts);
428 
429         final DevicePolicyManager dpm = mLockPatternUtils.getDevicePolicyManager();
430         final int failedAttemptsBeforeWipe =
431                 dpm.getMaximumFailedPasswordsForWipe(null, userId);
432 
433         final int remainingBeforeWipe = failedAttemptsBeforeWipe > 0 ?
434                 (failedAttemptsBeforeWipe - failedAttempts)
435                 : Integer.MAX_VALUE; // because DPM returns 0 if no restriction
436         if (remainingBeforeWipe < LockPatternUtils.FAILED_ATTEMPTS_BEFORE_WIPE_GRACE) {
437             // The user has installed a DevicePolicyManager that requests a user/profile to be wiped
438             // N attempts. Once we get below the grace period, we post this dialog every time as a
439             // clear warning until the deletion fires.
440             // Check which profile has the strictest policy for failed password attempts
441             final int expiringUser = dpm.getProfileWithMinimumFailedPasswordsForWipe(userId);
442             int userType = USER_TYPE_PRIMARY;
443             if (expiringUser == userId) {
444                 // TODO: http://b/23522538
445                 if (expiringUser != UserHandle.USER_SYSTEM) {
446                     userType = USER_TYPE_SECONDARY_USER;
447                 }
448             } else if (expiringUser != UserHandle.USER_NULL) {
449                 userType = USER_TYPE_WORK_PROFILE;
450             } // If USER_NULL, which shouldn't happen, leave it as USER_TYPE_PRIMARY
451             if (remainingBeforeWipe > 0) {
452                 showAlmostAtWipeDialog(failedAttempts, remainingBeforeWipe, userType);
453             } else {
454                 // Too many attempts. The device will be wiped shortly.
455                 Slog.i(TAG, "Too many unlock attempts; user " + expiringUser + " will be wiped!");
456                 showWipeDialog(failedAttempts, userType);
457             }
458         }
459         mLockPatternUtils.reportFailedPasswordAttempt(userId);
460         if (timeoutMs > 0) {
461             mLockPatternUtils.reportPasswordLockout(timeoutMs, userId);
462             showTimeoutDialog(userId, timeoutMs);
463         }
464     }
465 
466     /**
467      * Shows the primary security screen for the user. This will be either the multi-selector
468      * or the user's security method.
469      * @param turningOff true if the device is being turned off
470      */
showPrimarySecurityScreen(boolean turningOff)471     void showPrimarySecurityScreen(boolean turningOff) {
472         SecurityMode securityMode = mSecurityModel.getSecurityMode(
473                 KeyguardUpdateMonitor.getCurrentUser());
474         if (DEBUG) Log.v(TAG, "showPrimarySecurityScreen(turningOff=" + turningOff + ")");
475         showSecurityScreen(securityMode);
476     }
477 
478     /**
479      * Shows the next security screen if there is one.
480      * @param authenticated true if the user entered the correct authentication
481      * @param targetUserId a user that needs to be the foreground user at the finish (if called)
482      *     completion.
483      * @return true if keyguard is done
484      */
showNextSecurityScreenOrFinish(boolean authenticated, int targetUserId)485     boolean showNextSecurityScreenOrFinish(boolean authenticated, int targetUserId) {
486         if (DEBUG) Log.d(TAG, "showNextSecurityScreenOrFinish(" + authenticated + ")");
487         boolean finish = false;
488         boolean strongAuth = false;
489         int eventSubtype = -1;
490         if (mUpdateMonitor.getUserHasTrust(targetUserId)) {
491             finish = true;
492             eventSubtype = BOUNCER_DISMISS_EXTENDED_ACCESS;
493         } else if (mUpdateMonitor.getUserUnlockedWithBiometric(targetUserId)) {
494             finish = true;
495             eventSubtype = BOUNCER_DISMISS_BIOMETRIC;
496         } else if (SecurityMode.None == mCurrentSecuritySelection) {
497             SecurityMode securityMode = mSecurityModel.getSecurityMode(targetUserId);
498             if (SecurityMode.None == securityMode) {
499                 finish = true; // no security required
500                 eventSubtype = BOUNCER_DISMISS_NONE_SECURITY;
501             } else {
502                 showSecurityScreen(securityMode); // switch to the alternate security view
503             }
504         } else if (authenticated) {
505             switch (mCurrentSecuritySelection) {
506                 case Pattern:
507                 case Password:
508                 case PIN:
509                     strongAuth = true;
510                     finish = true;
511                     eventSubtype = BOUNCER_DISMISS_PASSWORD;
512                     break;
513 
514                 case SimPin:
515                 case SimPuk:
516                     // Shortcut for SIM PIN/PUK to go to directly to user's security screen or home
517                     SecurityMode securityMode = mSecurityModel.getSecurityMode(targetUserId);
518                     if (securityMode == SecurityMode.None && mLockPatternUtils.isLockScreenDisabled(
519                             KeyguardUpdateMonitor.getCurrentUser())) {
520                         finish = true;
521                         eventSubtype = BOUNCER_DISMISS_SIM;
522                     } else {
523                         showSecurityScreen(securityMode);
524                     }
525                     break;
526 
527                 default:
528                     Log.v(TAG, "Bad security screen " + mCurrentSecuritySelection + ", fail safe");
529                     showPrimarySecurityScreen(false);
530                     break;
531             }
532         }
533         if (eventSubtype != -1) {
534             mMetricsLogger.write(new LogMaker(MetricsEvent.BOUNCER)
535                     .setType(MetricsEvent.TYPE_DISMISS).setSubtype(eventSubtype));
536         }
537         if (finish) {
538             mSecurityCallback.finish(strongAuth, targetUserId);
539         }
540         return finish;
541     }
542 
543     /**
544      * Switches to the given security view unless it's already being shown, in which case
545      * this is a no-op.
546      *
547      * @param securityMode
548      */
showSecurityScreen(SecurityMode securityMode)549     private void showSecurityScreen(SecurityMode securityMode) {
550         if (DEBUG) Log.d(TAG, "showSecurityScreen(" + securityMode + ")");
551 
552         if (securityMode == mCurrentSecuritySelection) return;
553 
554         KeyguardSecurityView oldView = getSecurityView(mCurrentSecuritySelection);
555         KeyguardSecurityView newView = getSecurityView(securityMode);
556 
557         // Emulate Activity life cycle
558         if (oldView != null) {
559             oldView.onPause();
560             oldView.setKeyguardCallback(mNullCallback); // ignore requests from old view
561         }
562         if (securityMode != SecurityMode.None) {
563             newView.onResume(KeyguardSecurityView.VIEW_REVEALED);
564             newView.setKeyguardCallback(mCallback);
565         }
566 
567         // Find and show this child.
568         final int childCount = mSecurityViewFlipper.getChildCount();
569 
570         final int securityViewIdForMode = getSecurityViewIdForMode(securityMode);
571         for (int i = 0; i < childCount; i++) {
572             if (mSecurityViewFlipper.getChildAt(i).getId() == securityViewIdForMode) {
573                 mSecurityViewFlipper.setDisplayedChild(i);
574                 break;
575             }
576         }
577 
578         mCurrentSecuritySelection = securityMode;
579         mCurrentSecurityView = newView;
580         mSecurityCallback.onSecurityModeChanged(securityMode,
581                 securityMode != SecurityMode.None && newView.needsInput());
582     }
583 
getFlipper()584     private KeyguardSecurityViewFlipper getFlipper() {
585         for (int i = 0; i < getChildCount(); i++) {
586             View child = getChildAt(i);
587             if (child instanceof KeyguardSecurityViewFlipper) {
588                 return (KeyguardSecurityViewFlipper) child;
589             }
590         }
591         return null;
592     }
593 
594     private KeyguardSecurityCallback mCallback = new KeyguardSecurityCallback() {
595         public void userActivity() {
596             if (mSecurityCallback != null) {
597                 mSecurityCallback.userActivity();
598             }
599         }
600 
601         @Override
602         public void onUserInput() {
603             mUpdateMonitor.cancelFaceAuth();
604         }
605 
606         public void dismiss(boolean authenticated, int targetId) {
607             mSecurityCallback.dismiss(authenticated, targetId);
608         }
609 
610         public boolean isVerifyUnlockOnly() {
611             return mIsVerifyUnlockOnly;
612         }
613 
614         public void reportUnlockAttempt(int userId, boolean success, int timeoutMs) {
615             if (success) {
616                 StatsLog.write(StatsLog.KEYGUARD_BOUNCER_PASSWORD_ENTERED,
617                     StatsLog.KEYGUARD_BOUNCER_PASSWORD_ENTERED__RESULT__SUCCESS);
618                 mLockPatternUtils.reportSuccessfulPasswordAttempt(userId);
619                 // Force a garbage collection in an attempt to erase any lockscreen password left in
620                 // memory. Do it asynchronously with a 5-sec delay to avoid making the keyguard
621                 // dismiss animation janky.
622                 ThreadUtils.postOnBackgroundThread(() -> {
623                     try {
624                         Thread.sleep(5000);
625                     } catch (InterruptedException ignored) { }
626                     Runtime.getRuntime().gc();
627                 });
628             } else {
629                 StatsLog.write(StatsLog.KEYGUARD_BOUNCER_PASSWORD_ENTERED,
630                     StatsLog.KEYGUARD_BOUNCER_PASSWORD_ENTERED__RESULT__FAILURE);
631                 KeyguardSecurityContainer.this.reportFailedUnlockAttempt(userId, timeoutMs);
632             }
633             mMetricsLogger.write(new LogMaker(MetricsEvent.BOUNCER)
634                     .setType(success ? MetricsEvent.TYPE_SUCCESS : MetricsEvent.TYPE_FAILURE));
635         }
636 
637         public void reset() {
638             mSecurityCallback.reset();
639         }
640 
641         public void onCancelClicked() {
642             mSecurityCallback.onCancelClicked();
643         }
644     };
645 
646     // The following is used to ignore callbacks from SecurityViews that are no longer current
647     // (e.g. face unlock). This avoids unwanted asynchronous events from messing with the
648     // state for the current security method.
649     private KeyguardSecurityCallback mNullCallback = new KeyguardSecurityCallback() {
650         @Override
651         public void userActivity() { }
652         @Override
653         public void reportUnlockAttempt(int userId, boolean success, int timeoutMs) { }
654         @Override
655         public boolean isVerifyUnlockOnly() { return false; }
656         @Override
657         public void dismiss(boolean securityVerified, int targetUserId) { }
658         @Override
659         public void onUserInput() { }
660         @Override
661         public void reset() {}
662     };
663 
getSecurityViewIdForMode(SecurityMode securityMode)664     private int getSecurityViewIdForMode(SecurityMode securityMode) {
665         switch (securityMode) {
666             case Pattern: return R.id.keyguard_pattern_view;
667             case PIN: return R.id.keyguard_pin_view;
668             case Password: return R.id.keyguard_password_view;
669             case SimPin: return R.id.keyguard_sim_pin_view;
670             case SimPuk: return R.id.keyguard_sim_puk_view;
671         }
672         return 0;
673     }
674 
675     @VisibleForTesting
getLayoutIdFor(SecurityMode securityMode)676     public int getLayoutIdFor(SecurityMode securityMode) {
677         switch (securityMode) {
678             case Pattern: return R.layout.keyguard_pattern_view;
679             case PIN: return R.layout.keyguard_pin_view;
680             case Password: return R.layout.keyguard_password_view;
681             case SimPin: return R.layout.keyguard_sim_pin_view;
682             case SimPuk: return R.layout.keyguard_sim_puk_view;
683             default:
684                 return 0;
685         }
686     }
687 
getSecurityMode()688     public SecurityMode getSecurityMode() {
689         return mSecurityModel.getSecurityMode(KeyguardUpdateMonitor.getCurrentUser());
690     }
691 
getCurrentSecurityMode()692     public SecurityMode getCurrentSecurityMode() {
693         return mCurrentSecuritySelection;
694     }
695 
getCurrentSecurityView()696     public KeyguardSecurityView getCurrentSecurityView() {
697         return mCurrentSecurityView;
698     }
699 
verifyUnlock()700     public void verifyUnlock() {
701         mIsVerifyUnlockOnly = true;
702         showSecurityScreen(getSecurityMode());
703     }
704 
getCurrentSecuritySelection()705     public SecurityMode getCurrentSecuritySelection() {
706         return mCurrentSecuritySelection;
707     }
708 
dismiss(boolean authenticated, int targetUserId)709     public void dismiss(boolean authenticated, int targetUserId) {
710         mCallback.dismiss(authenticated, targetUserId);
711     }
712 
needsInput()713     public boolean needsInput() {
714         return mSecurityViewFlipper.needsInput();
715     }
716 
717     @Override
setKeyguardCallback(KeyguardSecurityCallback callback)718     public void setKeyguardCallback(KeyguardSecurityCallback callback) {
719         mSecurityViewFlipper.setKeyguardCallback(callback);
720     }
721 
722     @Override
reset()723     public void reset() {
724         mSecurityViewFlipper.reset();
725     }
726 
727     @Override
getCallback()728     public KeyguardSecurityCallback getCallback() {
729         return mSecurityViewFlipper.getCallback();
730     }
731 
732     @Override
showPromptReason(int reason)733     public void showPromptReason(int reason) {
734         if (mCurrentSecuritySelection != SecurityMode.None) {
735             if (reason != PROMPT_REASON_NONE) {
736                 Log.i(TAG, "Strong auth required, reason: " + reason);
737             }
738             getSecurityView(mCurrentSecuritySelection).showPromptReason(reason);
739         }
740     }
741 
showMessage(CharSequence message, ColorStateList colorState)742     public void showMessage(CharSequence message, ColorStateList colorState) {
743         if (mCurrentSecuritySelection != SecurityMode.None) {
744             getSecurityView(mCurrentSecuritySelection).showMessage(message, colorState);
745         }
746     }
747 
748     @Override
showUsabilityHint()749     public void showUsabilityHint() {
750         mSecurityViewFlipper.showUsabilityHint();
751     }
752 
753 }
754 
755