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