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