Home | History | Annotate | Download | only in keyguard
      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 {
    102         public boolean dismiss(boolean authenticated, int targetUserId);
    103         public void userActivity();
    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          */
    111         public void finish(boolean strongAuth, int targetUserId);
    112         public void reset();
    113         public void onCancelClicked();
    114     }
    115 
    116     public KeyguardSecurityContainer(Context context, AttributeSet attrs) {
    117         this(context, attrs, 0);
    118     }
    119 
    120     public KeyguardSecurityContainer(Context context) {
    121         this(context, null, 0);
    122     }
    123 
    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 
    135     public void setSecurityCallback(SecurityCallback callback) {
    136         mSecurityCallback = callback;
    137     }
    138 
    139     @Override
    140     public void onResume(int reason) {
    141         if (mCurrentSecuritySelection != SecurityMode.None) {
    142             getSecurityView(mCurrentSecuritySelection).onResume(reason);
    143         }
    144         updateBiometricRetry();
    145     }
    146 
    147     @Override
    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
    159     public boolean shouldDelayChildPressedState() {
    160         return true;
    161     }
    162 
    163     @Override
    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
    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 
    241     private void startSpringAnimation(float startVelocity) {
    242         mSpringAnimation
    243             .setStartVelocity(startVelocity)
    244             .animateToFinalPosition(0);
    245     }
    246 
    247     public void startAppearAnimation() {
    248         if (mCurrentSecuritySelection != SecurityMode.None) {
    249             getSecurityView(mCurrentSecuritySelection).startAppearAnimation();
    250         }
    251     }
    252 
    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      */
    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 
    273     public CharSequence getTitle() {
    274         return mSecurityViewFlipper.getTitle();
    275     }
    276 
    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 
    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 
    312     protected void onFinishInflate() {
    313         mSecurityViewFlipper = findViewById(R.id.view_flipper);
    314         mSecurityViewFlipper.setLockPatternUtils(mLockPatternUtils);
    315     }
    316 
    317     public void setLockPatternUtils(LockPatternUtils utils) {
    318         mLockPatternUtils = utils;
    319         mSecurityModel.setLockPatternUtils(utils);
    320         mSecurityViewFlipper.setLockPatternUtils(mLockPatternUtils);
    321     }
    322 
    323     @Override
    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 
    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 
    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 
    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 
    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 
    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      */
    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      */
    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      */
    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 
    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 
    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
    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 
    665     public SecurityMode getSecurityMode() {
    666         return mSecurityModel.getSecurityMode(KeyguardUpdateMonitor.getCurrentUser());
    667     }
    668 
    669     public SecurityMode getCurrentSecurityMode() {
    670         return mCurrentSecuritySelection;
    671     }
    672 
    673     public KeyguardSecurityView getCurrentSecurityView() {
    674         return mCurrentSecurityView;
    675     }
    676 
    677     public void verifyUnlock() {
    678         mIsVerifyUnlockOnly = true;
    679         showSecurityScreen(getSecurityMode());
    680     }
    681 
    682     public SecurityMode getCurrentSecuritySelection() {
    683         return mCurrentSecuritySelection;
    684     }
    685 
    686     public void dismiss(boolean authenticated, int targetUserId) {
    687         mCallback.dismiss(authenticated, targetUserId);
    688     }
    689 
    690     public boolean needsInput() {
    691         return mSecurityViewFlipper.needsInput();
    692     }
    693 
    694     @Override
    695     public void setKeyguardCallback(KeyguardSecurityCallback callback) {
    696         mSecurityViewFlipper.setKeyguardCallback(callback);
    697     }
    698 
    699     @Override
    700     public void reset() {
    701         mSecurityViewFlipper.reset();
    702     }
    703 
    704     @Override
    705     public KeyguardSecurityCallback getCallback() {
    706         return mSecurityViewFlipper.getCallback();
    707     }
    708 
    709     @Override
    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 
    719     public void showMessage(CharSequence message, ColorStateList colorState) {
    720         if (mCurrentSecuritySelection != SecurityMode.None) {
    721             getSecurityView(mCurrentSecuritySelection).showMessage(message, colorState);
    722         }
    723     }
    724 
    725     @Override
    726     public void showUsabilityHint() {
    727         mSecurityViewFlipper.showUsabilityHint();
    728     }
    729 
    730 }
    731 
    732