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