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.os.UserHandle;
     23 import android.os.UserManager;
     24 import android.util.AttributeSet;
     25 import android.util.Log;
     26 import android.util.Slog;
     27 import android.view.LayoutInflater;
     28 import android.view.View;
     29 import android.view.WindowManager;
     30 import android.widget.FrameLayout;
     31 
     32 import com.android.internal.widget.LockPatternUtils;
     33 import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
     34 
     35 public class KeyguardSecurityContainer extends FrameLayout implements KeyguardSecurityView {
     36     private static final boolean DEBUG = KeyguardConstants.DEBUG;
     37     private static final String TAG = "KeyguardSecurityView";
     38 
     39     private static final int USER_TYPE_PRIMARY = 1;
     40     private static final int USER_TYPE_WORK_PROFILE = 2;
     41     private static final int USER_TYPE_SECONDARY_USER = 3;
     42 
     43     private KeyguardSecurityModel mSecurityModel;
     44     private boolean mEnableFallback; // TODO: This should get the value from KeyguardPatternView
     45     private LockPatternUtils mLockPatternUtils;
     46 
     47     private KeyguardSecurityViewFlipper mSecurityViewFlipper;
     48     private boolean mIsVerifyUnlockOnly;
     49     private SecurityMode mCurrentSecuritySelection = SecurityMode.Invalid;
     50     private boolean mIsBouncing;
     51     private SecurityCallback mSecurityCallback;
     52 
     53     private final KeyguardUpdateMonitor mUpdateMonitor;
     54 
     55     // Used to notify the container when something interesting happens.
     56     public interface SecurityCallback {
     57         public boolean dismiss(boolean authenticated);
     58         public void userActivity();
     59         public void onSecurityModeChanged(SecurityMode securityMode, boolean needsInput);
     60         public void finish();
     61     }
     62 
     63     public KeyguardSecurityContainer(Context context, AttributeSet attrs) {
     64         this(context, attrs, 0);
     65     }
     66 
     67     public KeyguardSecurityContainer(Context context) {
     68         this(context, null, 0);
     69     }
     70 
     71     public KeyguardSecurityContainer(Context context, AttributeSet attrs, int defStyle) {
     72         super(context, attrs, defStyle);
     73         mSecurityModel = new KeyguardSecurityModel(context);
     74         mLockPatternUtils = new LockPatternUtils(context);
     75         mUpdateMonitor = KeyguardUpdateMonitor.getInstance(mContext);
     76     }
     77 
     78     public void setSecurityCallback(SecurityCallback callback) {
     79         mSecurityCallback = callback;
     80     }
     81 
     82     @Override
     83     public void onResume(int reason) {
     84         if (mCurrentSecuritySelection != SecurityMode.None) {
     85             getSecurityView(mCurrentSecuritySelection).onResume(reason);
     86         }
     87     }
     88 
     89     @Override
     90     public void onPause() {
     91         if (mCurrentSecuritySelection != SecurityMode.None) {
     92             getSecurityView(mCurrentSecuritySelection).onPause();
     93         }
     94     }
     95 
     96     public void startAppearAnimation() {
     97         if (mCurrentSecuritySelection != SecurityMode.None) {
     98             getSecurityView(mCurrentSecuritySelection).startAppearAnimation();
     99         }
    100     }
    101 
    102     public boolean startDisappearAnimation(Runnable onFinishRunnable) {
    103         if (mCurrentSecuritySelection != SecurityMode.None) {
    104             return getSecurityView(mCurrentSecuritySelection).startDisappearAnimation(
    105                     onFinishRunnable);
    106         }
    107         return false;
    108     }
    109 
    110     void updateSecurityViews(boolean isBouncing) {
    111         int children = mSecurityViewFlipper.getChildCount();
    112         for (int i = 0; i < children; i++) {
    113             updateSecurityView(mSecurityViewFlipper.getChildAt(i), isBouncing);
    114         }
    115     }
    116 
    117     public void announceCurrentSecurityMethod() {
    118         View v = (View) getSecurityView(mCurrentSecuritySelection);
    119         if (v != null) {
    120             v.announceForAccessibility(v.getContentDescription());
    121         }
    122     }
    123 
    124     public CharSequence getCurrentSecurityModeContentDescription() {
    125         View v = (View) getSecurityView(mCurrentSecuritySelection);
    126         if (v != null) {
    127             return v.getContentDescription();
    128         }
    129         return "";
    130     }
    131 
    132     private KeyguardSecurityView getSecurityView(SecurityMode securityMode) {
    133         final int securityViewIdForMode = getSecurityViewIdForMode(securityMode);
    134         KeyguardSecurityView view = null;
    135         final int children = mSecurityViewFlipper.getChildCount();
    136         for (int child = 0; child < children; child++) {
    137             if (mSecurityViewFlipper.getChildAt(child).getId() == securityViewIdForMode) {
    138                 view = ((KeyguardSecurityView)mSecurityViewFlipper.getChildAt(child));
    139                 break;
    140             }
    141         }
    142         int layoutId = getLayoutIdFor(securityMode);
    143         if (view == null && layoutId != 0) {
    144             final LayoutInflater inflater = LayoutInflater.from(mContext);
    145             if (DEBUG) Log.v(TAG, "inflating id = " + layoutId);
    146             View v = inflater.inflate(layoutId, mSecurityViewFlipper, false);
    147             mSecurityViewFlipper.addView(v);
    148             updateSecurityView(v, mIsBouncing);
    149             view = (KeyguardSecurityView)v;
    150         }
    151 
    152         return view;
    153     }
    154 
    155     private void updateSecurityView(View view, boolean isBouncing) {
    156         mIsBouncing = isBouncing;
    157         if (view instanceof KeyguardSecurityView) {
    158             KeyguardSecurityView ksv = (KeyguardSecurityView) view;
    159             ksv.setKeyguardCallback(mCallback);
    160             ksv.setLockPatternUtils(mLockPatternUtils);
    161             if (isBouncing) {
    162                 ksv.showBouncer(0);
    163             } else {
    164                 ksv.hideBouncer(0);
    165             }
    166         } else {
    167             Log.w(TAG, "View " + view + " is not a KeyguardSecurityView");
    168         }
    169     }
    170 
    171     protected void onFinishInflate() {
    172         mSecurityViewFlipper = (KeyguardSecurityViewFlipper) findViewById(R.id.view_flipper);
    173         mSecurityViewFlipper.setLockPatternUtils(mLockPatternUtils);
    174     }
    175 
    176     public void setLockPatternUtils(LockPatternUtils utils) {
    177         mLockPatternUtils = utils;
    178         mSecurityModel.setLockPatternUtils(utils);
    179         mSecurityViewFlipper.setLockPatternUtils(mLockPatternUtils);
    180     }
    181 
    182     private void showDialog(String title, String message) {
    183         final AlertDialog dialog = new AlertDialog.Builder(mContext)
    184             .setTitle(title)
    185             .setMessage(message)
    186             .setNeutralButton(R.string.ok, null)
    187             .create();
    188         if (!(mContext instanceof Activity)) {
    189             dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
    190         }
    191         dialog.show();
    192     }
    193 
    194     private void showTimeoutDialog() {
    195         int timeoutInSeconds = (int) LockPatternUtils.FAILED_ATTEMPT_TIMEOUT_MS / 1000;
    196         int messageId = 0;
    197 
    198         switch (mSecurityModel.getSecurityMode()) {
    199             case Pattern:
    200                 messageId = R.string.kg_too_many_failed_pattern_attempts_dialog_message;
    201                 break;
    202             case PIN:
    203                 messageId = R.string.kg_too_many_failed_pin_attempts_dialog_message;
    204                 break;
    205             case Password:
    206                 messageId = R.string.kg_too_many_failed_password_attempts_dialog_message;
    207                 break;
    208             // These don't have timeout dialogs.
    209             case Account:
    210             case Biometric:
    211             case Invalid:
    212             case None:
    213             case SimPin:
    214             case SimPuk:
    215                 break;
    216         }
    217 
    218         if (messageId != 0) {
    219             final String message = mContext.getString(messageId,
    220                     KeyguardUpdateMonitor.getInstance(mContext).getFailedUnlockAttempts(),
    221                     timeoutInSeconds);
    222             showDialog(null, message);
    223         }
    224     }
    225 
    226     private void showAlmostAtWipeDialog(int attempts, int remaining, int userType) {
    227         String message = null;
    228         switch (userType) {
    229             case USER_TYPE_PRIMARY:
    230                 message = mContext.getString(R.string.kg_failed_attempts_almost_at_wipe,
    231                         attempts, remaining);
    232                 break;
    233             case USER_TYPE_SECONDARY_USER:
    234                 message = mContext.getString(R.string.kg_failed_attempts_almost_at_erase_user,
    235                         attempts, remaining);
    236                 break;
    237             case USER_TYPE_WORK_PROFILE:
    238                 message = mContext.getString(R.string.kg_failed_attempts_almost_at_erase_profile,
    239                         attempts, remaining);
    240                 break;
    241         }
    242         showDialog(null, message);
    243     }
    244 
    245     private void showWipeDialog(int attempts, int userType) {
    246         String message = null;
    247         switch (userType) {
    248             case USER_TYPE_PRIMARY:
    249                 message = mContext.getString(R.string.kg_failed_attempts_now_wiping,
    250                         attempts);
    251                 break;
    252             case USER_TYPE_SECONDARY_USER:
    253                 message = mContext.getString(R.string.kg_failed_attempts_now_erasing_user,
    254                         attempts);
    255                 break;
    256             case USER_TYPE_WORK_PROFILE:
    257                 message = mContext.getString(R.string.kg_failed_attempts_now_erasing_profile,
    258                         attempts);
    259                 break;
    260         }
    261         showDialog(null, message);
    262     }
    263 
    264     private void showAlmostAtAccountLoginDialog() {
    265         final int timeoutInSeconds = (int) LockPatternUtils.FAILED_ATTEMPT_TIMEOUT_MS / 1000;
    266         final int count = LockPatternUtils.FAILED_ATTEMPTS_BEFORE_RESET
    267                 - LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT;
    268         String message = mContext.getString(R.string.kg_failed_attempts_almost_at_login,
    269                 count, LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT, timeoutInSeconds);
    270         showDialog(null, message);
    271     }
    272 
    273     private void reportFailedUnlockAttempt() {
    274         final KeyguardUpdateMonitor monitor = KeyguardUpdateMonitor.getInstance(mContext);
    275         final int failedAttempts = monitor.getFailedUnlockAttempts() + 1; // +1 for this time
    276 
    277         if (DEBUG) Log.d(TAG, "reportFailedPatternAttempt: #" + failedAttempts);
    278 
    279         SecurityMode mode = mSecurityModel.getSecurityMode();
    280         final boolean usingPattern = mode == KeyguardSecurityModel.SecurityMode.Pattern;
    281         final int currentUser = mLockPatternUtils.getCurrentUser();
    282         final DevicePolicyManager dpm = mLockPatternUtils.getDevicePolicyManager();
    283         final int failedAttemptsBeforeWipe =
    284                 dpm.getMaximumFailedPasswordsForWipe(null, currentUser);
    285 
    286         final int failedAttemptWarning = LockPatternUtils.FAILED_ATTEMPTS_BEFORE_RESET
    287                 - LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT;
    288 
    289         final int remainingBeforeWipe = failedAttemptsBeforeWipe > 0 ?
    290                 (failedAttemptsBeforeWipe - failedAttempts)
    291                 : Integer.MAX_VALUE; // because DPM returns 0 if no restriction
    292         boolean showTimeout = false;
    293         if (remainingBeforeWipe < LockPatternUtils.FAILED_ATTEMPTS_BEFORE_WIPE_GRACE) {
    294             // The user has installed a DevicePolicyManager that requests a user/profile to be wiped
    295             // N attempts. Once we get below the grace period, we post this dialog every time as a
    296             // clear warning until the deletion fires.
    297             // Check which profile has the strictest policy for failed password attempts
    298             final int expiringUser = dpm.getProfileWithMinimumFailedPasswordsForWipe(currentUser);
    299             int userType = USER_TYPE_PRIMARY;
    300             if (expiringUser == currentUser) {
    301                 if (expiringUser != UserHandle.USER_OWNER) {
    302                     userType = USER_TYPE_SECONDARY_USER;
    303                 }
    304             } else if (expiringUser != UserHandle.USER_NULL) {
    305                 userType = USER_TYPE_WORK_PROFILE;
    306             } // If USER_NULL, which shouldn't happen, leave it as USER_TYPE_PRIMARY
    307             if (remainingBeforeWipe > 0) {
    308                 showAlmostAtWipeDialog(failedAttempts, remainingBeforeWipe, userType);
    309             } else {
    310                 // Too many attempts. The device will be wiped shortly.
    311                 Slog.i(TAG, "Too many unlock attempts; user " + expiringUser + " will be wiped!");
    312                 showWipeDialog(failedAttempts, userType);
    313             }
    314         } else {
    315             showTimeout =
    316                 (failedAttempts % LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT) == 0;
    317             if (usingPattern && mEnableFallback) {
    318                 if (failedAttempts == failedAttemptWarning) {
    319                     showAlmostAtAccountLoginDialog();
    320                     showTimeout = false; // don't show both dialogs
    321                 } else if (failedAttempts >= LockPatternUtils.FAILED_ATTEMPTS_BEFORE_RESET) {
    322                     mLockPatternUtils.setPermanentlyLocked(true);
    323                     showSecurityScreen(SecurityMode.Account);
    324                     // don't show timeout dialog because we show account unlock screen next
    325                     showTimeout = false;
    326                 }
    327             }
    328         }
    329         monitor.reportFailedUnlockAttempt();
    330         mLockPatternUtils.reportFailedPasswordAttempt();
    331         if (showTimeout) {
    332             showTimeoutDialog();
    333         }
    334     }
    335 
    336     /**
    337      * Shows the primary security screen for the user. This will be either the multi-selector
    338      * or the user's security method.
    339      * @param turningOff true if the device is being turned off
    340      */
    341     void showPrimarySecurityScreen(boolean turningOff) {
    342         SecurityMode securityMode = mSecurityModel.getSecurityMode();
    343         if (DEBUG) Log.v(TAG, "showPrimarySecurityScreen(turningOff=" + turningOff + ")");
    344         if (!turningOff &&
    345                 KeyguardUpdateMonitor.getInstance(mContext).isAlternateUnlockEnabled()) {
    346             // If we're not turning off, then allow biometric alternate.
    347             // We'll reload it when the device comes back on.
    348             securityMode = mSecurityModel.getAlternateFor(securityMode);
    349         }
    350         showSecurityScreen(securityMode);
    351     }
    352 
    353     /**
    354      * Shows the backup security screen for the current security mode.  This could be used for
    355      * password recovery screens but is currently only used for pattern unlock to show the
    356      * account unlock screen and biometric unlock to show the user's normal unlock.
    357      */
    358     private void showBackupSecurityScreen() {
    359         if (DEBUG) Log.d(TAG, "showBackupSecurity()");
    360         SecurityMode backup = mSecurityModel.getBackupSecurityMode(mCurrentSecuritySelection);
    361         showSecurityScreen(backup);
    362     }
    363 
    364     /**
    365      * Shows the next security screen if there is one.
    366      * @param authenticated true if the user entered the correct authentication
    367      * @param authenticated
    368      * @return true if keyguard is done
    369      */
    370     boolean showNextSecurityScreenOrFinish(boolean authenticated) {
    371         if (DEBUG) Log.d(TAG, "showNextSecurityScreenOrFinish(" + authenticated + ")");
    372         boolean finish = false;
    373         if (mUpdateMonitor.getUserHasTrust(mLockPatternUtils.getCurrentUser())) {
    374             finish = true;
    375         } else if (SecurityMode.None == mCurrentSecuritySelection) {
    376             SecurityMode securityMode = mSecurityModel.getSecurityMode();
    377             // Allow an alternate, such as biometric unlock
    378             securityMode = mSecurityModel.getAlternateFor(securityMode);
    379             if (SecurityMode.None == securityMode) {
    380                 finish = true; // no security required
    381             } else {
    382                 showSecurityScreen(securityMode); // switch to the alternate security view
    383             }
    384         } else if (authenticated) {
    385             switch (mCurrentSecuritySelection) {
    386                 case Pattern:
    387                 case Password:
    388                 case PIN:
    389                 case Account:
    390                 case Biometric:
    391                     finish = true;
    392                     break;
    393 
    394                 case SimPin:
    395                 case SimPuk:
    396                     // Shortcut for SIM PIN/PUK to go to directly to user's security screen or home
    397                     SecurityMode securityMode = mSecurityModel.getSecurityMode();
    398                     if (securityMode != SecurityMode.None) {
    399                         showSecurityScreen(securityMode);
    400                     } else {
    401                         finish = true;
    402                     }
    403                     break;
    404 
    405                 default:
    406                     Log.v(TAG, "Bad security screen " + mCurrentSecuritySelection + ", fail safe");
    407                     showPrimarySecurityScreen(false);
    408                     break;
    409             }
    410         }
    411         if (finish) {
    412             mSecurityCallback.finish();
    413         }
    414         return finish;
    415     }
    416 
    417     /**
    418      * Switches to the given security view unless it's already being shown, in which case
    419      * this is a no-op.
    420      *
    421      * @param securityMode
    422      */
    423     private void showSecurityScreen(SecurityMode securityMode) {
    424         if (DEBUG) Log.d(TAG, "showSecurityScreen(" + securityMode + ")");
    425 
    426         if (securityMode == mCurrentSecuritySelection) return;
    427 
    428         KeyguardSecurityView oldView = getSecurityView(mCurrentSecuritySelection);
    429         KeyguardSecurityView newView = getSecurityView(securityMode);
    430 
    431         // Emulate Activity life cycle
    432         if (oldView != null) {
    433             oldView.onPause();
    434             oldView.setKeyguardCallback(mNullCallback); // ignore requests from old view
    435         }
    436         if (securityMode != SecurityMode.None) {
    437             newView.onResume(KeyguardSecurityView.VIEW_REVEALED);
    438             newView.setKeyguardCallback(mCallback);
    439         }
    440 
    441         // Find and show this child.
    442         final int childCount = mSecurityViewFlipper.getChildCount();
    443 
    444         final int securityViewIdForMode = getSecurityViewIdForMode(securityMode);
    445         for (int i = 0; i < childCount; i++) {
    446             if (mSecurityViewFlipper.getChildAt(i).getId() == securityViewIdForMode) {
    447                 mSecurityViewFlipper.setDisplayedChild(i);
    448                 break;
    449             }
    450         }
    451 
    452         mCurrentSecuritySelection = securityMode;
    453         mSecurityCallback.onSecurityModeChanged(securityMode,
    454                 securityMode != SecurityMode.None && newView.needsInput());
    455     }
    456 
    457     private KeyguardSecurityViewFlipper getFlipper() {
    458         for (int i = 0; i < getChildCount(); i++) {
    459             View child = getChildAt(i);
    460             if (child instanceof KeyguardSecurityViewFlipper) {
    461                 return (KeyguardSecurityViewFlipper) child;
    462             }
    463         }
    464         return null;
    465     }
    466 
    467     public void showBouncer(int duration) {
    468         KeyguardSecurityViewFlipper flipper = getFlipper();
    469         if (flipper != null) {
    470             flipper.showBouncer(duration);
    471         }
    472     }
    473 
    474     public void hideBouncer(int duration) {
    475         KeyguardSecurityViewFlipper flipper = getFlipper();
    476         if (flipper != null) {
    477             flipper.hideBouncer(duration);
    478         }
    479     }
    480 
    481     private KeyguardSecurityCallback mCallback = new KeyguardSecurityCallback() {
    482 
    483         public void userActivity() {
    484             if (mSecurityCallback != null) {
    485                 mSecurityCallback.userActivity();
    486             }
    487         }
    488 
    489         public void dismiss(boolean authenticated) {
    490             mSecurityCallback.dismiss(authenticated);
    491         }
    492 
    493         public boolean isVerifyUnlockOnly() {
    494             return mIsVerifyUnlockOnly;
    495         }
    496 
    497         public void reportUnlockAttempt(boolean success) {
    498             KeyguardUpdateMonitor monitor = KeyguardUpdateMonitor.getInstance(mContext);
    499             if (success) {
    500                 monitor.clearFailedUnlockAttempts();
    501                 mLockPatternUtils.reportSuccessfulPasswordAttempt();
    502             } else {
    503                 if (mCurrentSecuritySelection == SecurityMode.Biometric) {
    504                     monitor.reportFailedBiometricUnlockAttempt();
    505                 } else {
    506                     KeyguardSecurityContainer.this.reportFailedUnlockAttempt();
    507                 }
    508             }
    509         }
    510 
    511         @Override
    512         public void showBackupSecurity() {
    513             KeyguardSecurityContainer.this.showBackupSecurityScreen();
    514         }
    515 
    516     };
    517 
    518     // The following is used to ignore callbacks from SecurityViews that are no longer current
    519     // (e.g. face unlock). This avoids unwanted asynchronous events from messing with the
    520     // state for the current security method.
    521     private KeyguardSecurityCallback mNullCallback = new KeyguardSecurityCallback() {
    522         @Override
    523         public void userActivity() { }
    524         @Override
    525         public void showBackupSecurity() { }
    526         @Override
    527         public void reportUnlockAttempt(boolean success) { }
    528         @Override
    529         public boolean isVerifyUnlockOnly() { return false; }
    530         @Override
    531         public void dismiss(boolean securityVerified) { }
    532     };
    533 
    534     private int getSecurityViewIdForMode(SecurityMode securityMode) {
    535         switch (securityMode) {
    536             case Pattern: return R.id.keyguard_pattern_view;
    537             case PIN: return R.id.keyguard_pin_view;
    538             case Password: return R.id.keyguard_password_view;
    539             case Biometric: return R.id.keyguard_face_unlock_view;
    540             case Account: return R.id.keyguard_account_view;
    541             case SimPin: return R.id.keyguard_sim_pin_view;
    542             case SimPuk: return R.id.keyguard_sim_puk_view;
    543         }
    544         return 0;
    545     }
    546 
    547     private int getLayoutIdFor(SecurityMode securityMode) {
    548         switch (securityMode) {
    549             case Pattern: return R.layout.keyguard_pattern_view;
    550             case PIN: return R.layout.keyguard_pin_view;
    551             case Password: return R.layout.keyguard_password_view;
    552             case Biometric: return R.layout.keyguard_face_unlock_view;
    553             case Account: return R.layout.keyguard_account_view;
    554             case SimPin: return R.layout.keyguard_sim_pin_view;
    555             case SimPuk: return R.layout.keyguard_sim_puk_view;
    556             default:
    557                 return 0;
    558         }
    559     }
    560 
    561     public SecurityMode getSecurityMode() {
    562         return mSecurityModel.getSecurityMode();
    563     }
    564 
    565     public SecurityMode getCurrentSecurityMode() {
    566         return mCurrentSecuritySelection;
    567     }
    568 
    569     public void verifyUnlock() {
    570         mIsVerifyUnlockOnly = true;
    571         showSecurityScreen(getSecurityMode());
    572     }
    573 
    574     public SecurityMode getCurrentSecuritySelection() {
    575         return mCurrentSecuritySelection;
    576     }
    577 
    578     public void dismiss(boolean authenticated) {
    579         mCallback.dismiss(authenticated);
    580     }
    581 
    582     public boolean needsInput() {
    583         return mSecurityViewFlipper.needsInput();
    584     }
    585 
    586     @Override
    587     public void setKeyguardCallback(KeyguardSecurityCallback callback) {
    588         mSecurityViewFlipper.setKeyguardCallback(callback);
    589     }
    590 
    591     @Override
    592     public void reset() {
    593         mSecurityViewFlipper.reset();
    594     }
    595 
    596     @Override
    597     public KeyguardSecurityCallback getCallback() {
    598         return mSecurityViewFlipper.getCallback();
    599     }
    600 
    601     @Override
    602     public void showUsabilityHint() {
    603         mSecurityViewFlipper.showUsabilityHint();
    604     }
    605 
    606 }
    607 
    608