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