Home | History | Annotate | Download | only in settings
      1 /*
      2  * Copyright (C) 2010 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 
     17 package com.android.settings;
     18 
     19 import com.android.internal.logging.MetricsLogger;
     20 import com.android.internal.widget.LockPatternChecker;
     21 import com.android.internal.widget.LockPatternUtils;
     22 import com.android.internal.widget.PasswordEntryKeyboardHelper;
     23 import com.android.internal.widget.PasswordEntryKeyboardView;
     24 import com.android.internal.widget.TextViewInputDisabler;
     25 import com.android.settings.notification.RedactionInterstitial;
     26 
     27 import android.app.Activity;
     28 import android.app.Fragment;
     29 import android.app.admin.DevicePolicyManager;
     30 import android.content.Context;
     31 import android.content.Intent;
     32 import android.inputmethodservice.KeyboardView;
     33 import android.os.AsyncTask;
     34 import android.os.Bundle;
     35 import android.os.Handler;
     36 import android.os.Message;
     37 import android.os.UserHandle;
     38 import android.text.Editable;
     39 import android.text.InputType;
     40 import android.text.Selection;
     41 import android.text.Spannable;
     42 import android.text.TextUtils;
     43 import android.text.TextWatcher;
     44 import android.util.Log;
     45 import android.view.KeyEvent;
     46 import android.view.LayoutInflater;
     47 import android.view.View;
     48 import android.view.ViewGroup;
     49 import android.view.View.OnClickListener;
     50 import android.view.inputmethod.EditorInfo;
     51 import android.widget.Button;
     52 import android.widget.TextView;
     53 import android.widget.TextView.OnEditorActionListener;
     54 
     55 public class ChooseLockPassword extends SettingsActivity {
     56     public static final String PASSWORD_MIN_KEY = "lockscreen.password_min";
     57     public static final String PASSWORD_MAX_KEY = "lockscreen.password_max";
     58     public static final String PASSWORD_MIN_LETTERS_KEY = "lockscreen.password_min_letters";
     59     public static final String PASSWORD_MIN_LOWERCASE_KEY = "lockscreen.password_min_lowercase";
     60     public static final String PASSWORD_MIN_UPPERCASE_KEY = "lockscreen.password_min_uppercase";
     61     public static final String PASSWORD_MIN_NUMERIC_KEY = "lockscreen.password_min_numeric";
     62     public static final String PASSWORD_MIN_SYMBOLS_KEY = "lockscreen.password_min_symbols";
     63     public static final String PASSWORD_MIN_NONLETTER_KEY = "lockscreen.password_min_nonletter";
     64 
     65     private static final String TAG = "ChooseLockPassword";
     66 
     67     @Override
     68     public Intent getIntent() {
     69         Intent modIntent = new Intent(super.getIntent());
     70         modIntent.putExtra(EXTRA_SHOW_FRAGMENT, getFragmentClass().getName());
     71         return modIntent;
     72     }
     73 
     74     public static Intent createIntent(Context context, int quality,
     75             int minLength, final int maxLength, boolean requirePasswordToDecrypt,
     76             boolean confirmCredentials) {
     77         Intent intent = new Intent().setClass(context, ChooseLockPassword.class);
     78         intent.putExtra(LockPatternUtils.PASSWORD_TYPE_KEY, quality);
     79         intent.putExtra(PASSWORD_MIN_KEY, minLength);
     80         intent.putExtra(PASSWORD_MAX_KEY, maxLength);
     81         intent.putExtra(ChooseLockGeneric.CONFIRM_CREDENTIALS, confirmCredentials);
     82         intent.putExtra(EncryptionInterstitial.EXTRA_REQUIRE_PASSWORD, requirePasswordToDecrypt);
     83         return intent;
     84     }
     85 
     86     public static Intent createIntent(Context context, int quality,
     87             int minLength, final int maxLength, boolean requirePasswordToDecrypt, String password) {
     88         Intent intent = createIntent(context, quality, minLength, maxLength,
     89                 requirePasswordToDecrypt, false);
     90         intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD, password);
     91         return intent;
     92     }
     93 
     94     public static Intent createIntent(Context context, int quality,
     95             int minLength, final int maxLength, boolean requirePasswordToDecrypt, long challenge) {
     96         Intent intent = createIntent(context, quality, minLength, maxLength,
     97                 requirePasswordToDecrypt, false);
     98         intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_HAS_CHALLENGE, true);
     99         intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE, challenge);
    100         return intent;
    101     }
    102 
    103     @Override
    104     protected boolean isValidFragment(String fragmentName) {
    105         if (ChooseLockPasswordFragment.class.getName().equals(fragmentName)) return true;
    106         return false;
    107     }
    108 
    109     /* package */ Class<? extends Fragment> getFragmentClass() {
    110         return ChooseLockPasswordFragment.class;
    111     }
    112 
    113     @Override
    114     public void onCreate(Bundle savedInstanceState) {
    115         // TODO: Fix on phones
    116         // Disable IME on our window since we provide our own keyboard
    117         //getWindow().setFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM,
    118                 //WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM);
    119         super.onCreate(savedInstanceState);
    120         CharSequence msg = getText(R.string.lockpassword_choose_your_password_header);
    121         setTitle(msg);
    122     }
    123 
    124     public static class ChooseLockPasswordFragment extends InstrumentedFragment
    125             implements OnClickListener, OnEditorActionListener,  TextWatcher {
    126         private static final String KEY_FIRST_PIN = "first_pin";
    127         private static final String KEY_UI_STAGE = "ui_stage";
    128         private static final String KEY_CURRENT_PASSWORD = "current_password";
    129 
    130         private String mCurrentPassword;
    131         private boolean mHasChallenge;
    132         private long mChallenge;
    133         private TextView mPasswordEntry;
    134         private TextViewInputDisabler mPasswordEntryInputDisabler;
    135         private int mPasswordMinLength = LockPatternUtils.MIN_LOCK_PASSWORD_SIZE;
    136         private int mPasswordMaxLength = 16;
    137         private int mPasswordMinLetters = 0;
    138         private int mPasswordMinUpperCase = 0;
    139         private int mPasswordMinLowerCase = 0;
    140         private int mPasswordMinSymbols = 0;
    141         private int mPasswordMinNumeric = 0;
    142         private int mPasswordMinNonLetter = 0;
    143         private LockPatternUtils mLockPatternUtils;
    144         private AsyncTask<?, ?, ?> mPendingLockCheck;
    145         private int mRequestedQuality = DevicePolicyManager.PASSWORD_QUALITY_NUMERIC;
    146         private ChooseLockSettingsHelper mChooseLockSettingsHelper;
    147         private Stage mUiStage = Stage.Introduction;
    148         private boolean mDone = false;
    149         private TextView mHeaderText;
    150         private String mFirstPin;
    151         private KeyboardView mKeyboardView;
    152         private PasswordEntryKeyboardHelper mKeyboardHelper;
    153         private boolean mIsAlphaMode;
    154         private Button mCancelButton;
    155         private Button mNextButton;
    156         private static final int CONFIRM_EXISTING_REQUEST = 58;
    157         static final int RESULT_FINISHED = RESULT_FIRST_USER;
    158         private static final long ERROR_MESSAGE_TIMEOUT = 3000;
    159         private static final int MSG_SHOW_ERROR = 1;
    160 
    161         private Handler mHandler = new Handler() {
    162             @Override
    163             public void handleMessage(Message msg) {
    164                 if (msg.what == MSG_SHOW_ERROR) {
    165                     updateStage((Stage) msg.obj);
    166                 }
    167             }
    168         };
    169 
    170         /**
    171          * Keep track internally of where the user is in choosing a pattern.
    172          */
    173         protected enum Stage {
    174 
    175             Introduction(R.string.lockpassword_choose_your_password_header,
    176                     R.string.lockpassword_choose_your_pin_header,
    177                     R.string.lockpassword_continue_label),
    178 
    179             NeedToConfirm(R.string.lockpassword_confirm_your_password_header,
    180                     R.string.lockpassword_confirm_your_pin_header,
    181                     R.string.lockpassword_ok_label),
    182 
    183             ConfirmWrong(R.string.lockpassword_confirm_passwords_dont_match,
    184                     R.string.lockpassword_confirm_pins_dont_match,
    185                     R.string.lockpassword_continue_label);
    186 
    187             Stage(int hintInAlpha, int hintInNumeric, int nextButtonText) {
    188                 this.alphaHint = hintInAlpha;
    189                 this.numericHint = hintInNumeric;
    190                 this.buttonText = nextButtonText;
    191             }
    192 
    193             public final int alphaHint;
    194             public final int numericHint;
    195             public final int buttonText;
    196         }
    197 
    198         // required constructor for fragments
    199         public ChooseLockPasswordFragment() {
    200 
    201         }
    202 
    203         @Override
    204         public void onCreate(Bundle savedInstanceState) {
    205             super.onCreate(savedInstanceState);
    206             mLockPatternUtils = new LockPatternUtils(getActivity());
    207             Intent intent = getActivity().getIntent();
    208             if (!(getActivity() instanceof ChooseLockPassword)) {
    209                 throw new SecurityException("Fragment contained in wrong activity");
    210             }
    211             mRequestedQuality = Math.max(intent.getIntExtra(LockPatternUtils.PASSWORD_TYPE_KEY,
    212                     mRequestedQuality), mLockPatternUtils.getRequestedPasswordQuality(
    213                     UserHandle.myUserId()));
    214             mPasswordMinLength = Math.max(Math.max(
    215                     LockPatternUtils.MIN_LOCK_PASSWORD_SIZE,
    216                     intent.getIntExtra(PASSWORD_MIN_KEY, mPasswordMinLength)),
    217                     mLockPatternUtils.getRequestedMinimumPasswordLength(UserHandle.myUserId()));
    218             mPasswordMaxLength = intent.getIntExtra(PASSWORD_MAX_KEY, mPasswordMaxLength);
    219             mPasswordMinLetters = Math.max(intent.getIntExtra(PASSWORD_MIN_LETTERS_KEY,
    220                     mPasswordMinLetters), mLockPatternUtils.getRequestedPasswordMinimumLetters(
    221                     UserHandle.myUserId()));
    222             mPasswordMinUpperCase = Math.max(intent.getIntExtra(PASSWORD_MIN_UPPERCASE_KEY,
    223                     mPasswordMinUpperCase), mLockPatternUtils.getRequestedPasswordMinimumUpperCase(
    224                     UserHandle.myUserId()));
    225             mPasswordMinLowerCase = Math.max(intent.getIntExtra(PASSWORD_MIN_LOWERCASE_KEY,
    226                     mPasswordMinLowerCase), mLockPatternUtils.getRequestedPasswordMinimumLowerCase(
    227                     UserHandle.myUserId()));
    228             mPasswordMinNumeric = Math.max(intent.getIntExtra(PASSWORD_MIN_NUMERIC_KEY,
    229                     mPasswordMinNumeric), mLockPatternUtils.getRequestedPasswordMinimumNumeric(
    230                     UserHandle.myUserId()));
    231             mPasswordMinSymbols = Math.max(intent.getIntExtra(PASSWORD_MIN_SYMBOLS_KEY,
    232                     mPasswordMinSymbols), mLockPatternUtils.getRequestedPasswordMinimumSymbols(
    233                     UserHandle.myUserId()));
    234             mPasswordMinNonLetter = Math.max(intent.getIntExtra(PASSWORD_MIN_NONLETTER_KEY,
    235                     mPasswordMinNonLetter), mLockPatternUtils.getRequestedPasswordMinimumNonLetter(
    236                     UserHandle.myUserId()));
    237 
    238             mChooseLockSettingsHelper = new ChooseLockSettingsHelper(getActivity());
    239         }
    240 
    241         @Override
    242         public View onCreateView(LayoutInflater inflater, ViewGroup container,
    243                 Bundle savedInstanceState) {
    244             return inflater.inflate(R.layout.choose_lock_password, container, false);
    245         }
    246 
    247         @Override
    248         public void onViewCreated(View view, Bundle savedInstanceState) {
    249             super.onViewCreated(view, savedInstanceState);
    250 
    251             mCancelButton = (Button) view.findViewById(R.id.cancel_button);
    252             mCancelButton.setOnClickListener(this);
    253             mNextButton = (Button) view.findViewById(R.id.next_button);
    254             mNextButton.setOnClickListener(this);
    255 
    256             mIsAlphaMode = DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC == mRequestedQuality
    257                     || DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC == mRequestedQuality
    258                     || DevicePolicyManager.PASSWORD_QUALITY_COMPLEX == mRequestedQuality;
    259             mKeyboardView = (PasswordEntryKeyboardView) view.findViewById(R.id.keyboard);
    260             mPasswordEntry = (TextView) view.findViewById(R.id.password_entry);
    261             mPasswordEntry.setOnEditorActionListener(this);
    262             mPasswordEntry.addTextChangedListener(this);
    263             mPasswordEntryInputDisabler = new TextViewInputDisabler(mPasswordEntry);
    264 
    265             final Activity activity = getActivity();
    266             mKeyboardHelper = new PasswordEntryKeyboardHelper(activity,
    267                     mKeyboardView, mPasswordEntry);
    268             mKeyboardHelper.setKeyboardMode(mIsAlphaMode ?
    269                     PasswordEntryKeyboardHelper.KEYBOARD_MODE_ALPHA
    270                     : PasswordEntryKeyboardHelper.KEYBOARD_MODE_NUMERIC);
    271 
    272             mHeaderText = (TextView) view.findViewById(R.id.headerText);
    273             mKeyboardView.requestFocus();
    274 
    275             int currentType = mPasswordEntry.getInputType();
    276             mPasswordEntry.setInputType(mIsAlphaMode ? currentType
    277                     : (InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_VARIATION_PASSWORD));
    278 
    279             Intent intent = getActivity().getIntent();
    280             final boolean confirmCredentials = intent.getBooleanExtra(
    281                     ChooseLockGeneric.CONFIRM_CREDENTIALS, true);
    282             mCurrentPassword = intent.getStringExtra(ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD);
    283             mHasChallenge = intent.getBooleanExtra(
    284                     ChooseLockSettingsHelper.EXTRA_KEY_HAS_CHALLENGE, false);
    285             mChallenge = intent.getLongExtra(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE, 0);
    286             if (savedInstanceState == null) {
    287                 updateStage(Stage.Introduction);
    288                 if (confirmCredentials) {
    289                     mChooseLockSettingsHelper.launchConfirmationActivity(CONFIRM_EXISTING_REQUEST,
    290                             getString(R.string.unlock_set_unlock_launch_picker_title), true);
    291                 }
    292             } else {
    293                 // restore from previous state
    294                 mFirstPin = savedInstanceState.getString(KEY_FIRST_PIN);
    295                 final String state = savedInstanceState.getString(KEY_UI_STAGE);
    296                 if (state != null) {
    297                     mUiStage = Stage.valueOf(state);
    298                     updateStage(mUiStage);
    299                 }
    300 
    301                 if (mCurrentPassword == null) {
    302                     mCurrentPassword = savedInstanceState.getString(KEY_CURRENT_PASSWORD);
    303                 }
    304             }
    305             mDone = false;
    306             if (activity instanceof SettingsActivity) {
    307                 final SettingsActivity sa = (SettingsActivity) activity;
    308                 int id = mIsAlphaMode ? R.string.lockpassword_choose_your_password_header
    309                         : R.string.lockpassword_choose_your_pin_header;
    310                 CharSequence title = getText(id);
    311                 sa.setTitle(title);
    312             }
    313         }
    314 
    315         @Override
    316         protected int getMetricsCategory() {
    317             return MetricsLogger.CHOOSE_LOCK_PASSWORD;
    318         }
    319 
    320         @Override
    321         public void onResume() {
    322             super.onResume();
    323             updateStage(mUiStage);
    324             mPasswordEntryInputDisabler.setInputEnabled(true);
    325             mKeyboardView.requestFocus();
    326         }
    327 
    328         @Override
    329         public void onPause() {
    330             mHandler.removeMessages(MSG_SHOW_ERROR);
    331             if (mPendingLockCheck != null) {
    332                 mPendingLockCheck.cancel(false);
    333                 mPendingLockCheck = null;
    334             }
    335 
    336             super.onPause();
    337         }
    338 
    339         @Override
    340         public void onSaveInstanceState(Bundle outState) {
    341             super.onSaveInstanceState(outState);
    342             outState.putString(KEY_UI_STAGE, mUiStage.name());
    343             outState.putString(KEY_FIRST_PIN, mFirstPin);
    344             outState.putString(KEY_CURRENT_PASSWORD, mCurrentPassword);
    345         }
    346 
    347         @Override
    348         public void onActivityResult(int requestCode, int resultCode,
    349                 Intent data) {
    350             super.onActivityResult(requestCode, resultCode, data);
    351             switch (requestCode) {
    352                 case CONFIRM_EXISTING_REQUEST:
    353                     if (resultCode != Activity.RESULT_OK) {
    354                         getActivity().setResult(RESULT_FINISHED);
    355                         getActivity().finish();
    356                     } else {
    357                         mCurrentPassword = data.getStringExtra(
    358                                 ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD);
    359                     }
    360                     break;
    361             }
    362         }
    363 
    364         protected Intent getRedactionInterstitialIntent(Context context) {
    365             return RedactionInterstitial.createStartIntent(context);
    366         }
    367 
    368         protected void updateStage(Stage stage) {
    369             final Stage previousStage = mUiStage;
    370             mUiStage = stage;
    371             updateUi();
    372 
    373             // If the stage changed, announce the header for accessibility. This
    374             // is a no-op when accessibility is disabled.
    375             if (previousStage != stage) {
    376                 mHeaderText.announceForAccessibility(mHeaderText.getText());
    377             }
    378         }
    379 
    380         /**
    381          * Validates PIN and returns a message to display if PIN fails test.
    382          * @param password the raw password the user typed in
    383          * @return error message to show to user or null if password is OK
    384          */
    385         private String validatePassword(String password) {
    386             if (password.length() < mPasswordMinLength) {
    387                 return getString(mIsAlphaMode ?
    388                         R.string.lockpassword_password_too_short
    389                         : R.string.lockpassword_pin_too_short, mPasswordMinLength);
    390             }
    391             if (password.length() > mPasswordMaxLength) {
    392                 return getString(mIsAlphaMode ?
    393                         R.string.lockpassword_password_too_long
    394                         : R.string.lockpassword_pin_too_long, mPasswordMaxLength + 1);
    395             }
    396             int letters = 0;
    397             int numbers = 0;
    398             int lowercase = 0;
    399             int symbols = 0;
    400             int uppercase = 0;
    401             int nonletter = 0;
    402             for (int i = 0; i < password.length(); i++) {
    403                 char c = password.charAt(i);
    404                 // allow non control Latin-1 characters only
    405                 if (c < 32 || c > 127) {
    406                     return getString(R.string.lockpassword_illegal_character);
    407                 }
    408                 if (c >= '0' && c <= '9') {
    409                     numbers++;
    410                     nonletter++;
    411                 } else if (c >= 'A' && c <= 'Z') {
    412                     letters++;
    413                     uppercase++;
    414                 } else if (c >= 'a' && c <= 'z') {
    415                     letters++;
    416                     lowercase++;
    417                 } else {
    418                     symbols++;
    419                     nonletter++;
    420                 }
    421             }
    422             if (DevicePolicyManager.PASSWORD_QUALITY_NUMERIC == mRequestedQuality
    423                     || DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX == mRequestedQuality) {
    424                 if (letters > 0 || symbols > 0) {
    425                     // This shouldn't be possible unless user finds some way to bring up
    426                     // soft keyboard
    427                     return getString(R.string.lockpassword_pin_contains_non_digits);
    428                 }
    429                 // Check for repeated characters or sequences (e.g. '1234', '0000', '2468')
    430                 final int sequence = LockPatternUtils.maxLengthSequence(password);
    431                 if (DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX == mRequestedQuality
    432                         && sequence > LockPatternUtils.MAX_ALLOWED_SEQUENCE) {
    433                     return getString(R.string.lockpassword_pin_no_sequential_digits);
    434                 }
    435             } else if (DevicePolicyManager.PASSWORD_QUALITY_COMPLEX == mRequestedQuality) {
    436                 if (letters < mPasswordMinLetters) {
    437                     return String.format(getResources().getQuantityString(
    438                             R.plurals.lockpassword_password_requires_letters, mPasswordMinLetters),
    439                             mPasswordMinLetters);
    440                 } else if (numbers < mPasswordMinNumeric) {
    441                     return String.format(getResources().getQuantityString(
    442                             R.plurals.lockpassword_password_requires_numeric, mPasswordMinNumeric),
    443                             mPasswordMinNumeric);
    444                 } else if (lowercase < mPasswordMinLowerCase) {
    445                     return String.format(getResources().getQuantityString(
    446                             R.plurals.lockpassword_password_requires_lowercase, mPasswordMinLowerCase),
    447                             mPasswordMinLowerCase);
    448                 } else if (uppercase < mPasswordMinUpperCase) {
    449                     return String.format(getResources().getQuantityString(
    450                             R.plurals.lockpassword_password_requires_uppercase, mPasswordMinUpperCase),
    451                             mPasswordMinUpperCase);
    452                 } else if (symbols < mPasswordMinSymbols) {
    453                     return String.format(getResources().getQuantityString(
    454                             R.plurals.lockpassword_password_requires_symbols, mPasswordMinSymbols),
    455                             mPasswordMinSymbols);
    456                 } else if (nonletter < mPasswordMinNonLetter) {
    457                     return String.format(getResources().getQuantityString(
    458                             R.plurals.lockpassword_password_requires_nonletter, mPasswordMinNonLetter),
    459                             mPasswordMinNonLetter);
    460                 }
    461             } else {
    462                 final boolean alphabetic = DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC
    463                         == mRequestedQuality;
    464                 final boolean alphanumeric = DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC
    465                         == mRequestedQuality;
    466                 if ((alphabetic || alphanumeric) && letters == 0) {
    467                     return getString(R.string.lockpassword_password_requires_alpha);
    468                 }
    469                 if (alphanumeric && numbers == 0) {
    470                     return getString(R.string.lockpassword_password_requires_digit);
    471                 }
    472             }
    473             if(mLockPatternUtils.checkPasswordHistory(password, UserHandle.myUserId())) {
    474                 return getString(mIsAlphaMode ? R.string.lockpassword_password_recently_used
    475                         : R.string.lockpassword_pin_recently_used);
    476             }
    477 
    478             return null;
    479         }
    480 
    481         public void handleNext() {
    482             if (mDone) return;
    483 
    484             final String pin = mPasswordEntry.getText().toString();
    485             if (TextUtils.isEmpty(pin)) {
    486                 return;
    487             }
    488             String errorMsg = null;
    489             if (mUiStage == Stage.Introduction) {
    490                 errorMsg = validatePassword(pin);
    491                 if (errorMsg == null) {
    492                     mFirstPin = pin;
    493                     mPasswordEntry.setText("");
    494                     updateStage(Stage.NeedToConfirm);
    495                 }
    496             } else if (mUiStage == Stage.NeedToConfirm) {
    497                 if (mFirstPin.equals(pin)) {
    498                     boolean wasSecureBefore = mLockPatternUtils.isSecure(UserHandle.myUserId());
    499                     final boolean required = getActivity().getIntent().getBooleanExtra(
    500                             EncryptionInterstitial.EXTRA_REQUIRE_PASSWORD, true);
    501                     mLockPatternUtils.setCredentialRequiredToDecrypt(required);
    502                     mLockPatternUtils.saveLockPassword(pin, mCurrentPassword, mRequestedQuality,
    503                             UserHandle.myUserId());
    504 
    505                     if (mHasChallenge) {
    506                         startVerifyPassword(pin, wasSecureBefore);
    507                         return;
    508                     } else {
    509                         getActivity().setResult(RESULT_FINISHED);
    510                     }
    511                     finishConfirmStage(wasSecureBefore);
    512                 } else {
    513                     CharSequence tmp = mPasswordEntry.getText();
    514                     if (tmp != null) {
    515                         Selection.setSelection((Spannable) tmp, 0, tmp.length());
    516                     }
    517                     updateStage(Stage.ConfirmWrong);
    518                 }
    519             }
    520             if (errorMsg != null) {
    521                 showError(errorMsg, mUiStage);
    522             }
    523         }
    524 
    525         private void startVerifyPassword(final String pin, final boolean wasSecureBefore) {
    526             mPasswordEntryInputDisabler.setInputEnabled(false);
    527             setNextEnabled(false);
    528             if (mPendingLockCheck != null) {
    529                 mPendingLockCheck.cancel(false);
    530             }
    531 
    532             mPendingLockCheck = LockPatternChecker.verifyPassword(
    533                     mLockPatternUtils,
    534                     pin,
    535                     mChallenge,
    536                     UserHandle.myUserId(),
    537                     new LockPatternChecker.OnVerifyCallback() {
    538                         @Override
    539                         public void onVerified(byte[] token, int timeoutMs) {
    540                             if (token == null) {
    541                                 Log.e(TAG, "critical: no token returned from known good password");
    542                             }
    543 
    544                             mPasswordEntryInputDisabler.setInputEnabled(true);
    545                             setNextEnabled(true);
    546                             mPendingLockCheck = null;
    547 
    548                             Intent intent = new Intent();
    549                             intent.putExtra(
    550                                     ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN,
    551                                     token);
    552                             getActivity().setResult(RESULT_FINISHED, intent);
    553                             finishConfirmStage(wasSecureBefore);
    554                         }
    555                     });
    556         }
    557 
    558         private void finishConfirmStage(boolean wasSecureBefore) {
    559             getActivity().finish();
    560             mDone = true;
    561             if (!wasSecureBefore) {
    562                 Intent intent = getRedactionInterstitialIntent(getActivity());
    563                 if (intent != null) {
    564                     startActivity(intent);
    565                 }
    566             }
    567         }
    568 
    569         protected void setNextEnabled(boolean enabled) {
    570             mNextButton.setEnabled(enabled);
    571         }
    572 
    573         protected void setNextText(int text) {
    574             mNextButton.setText(text);
    575         }
    576 
    577         public void onClick(View v) {
    578             switch (v.getId()) {
    579                 case R.id.next_button:
    580                     handleNext();
    581                     break;
    582 
    583                 case R.id.cancel_button:
    584                     getActivity().finish();
    585                     break;
    586             }
    587         }
    588 
    589         private void showError(String msg, final Stage next) {
    590             mHeaderText.setText(msg);
    591             mHeaderText.announceForAccessibility(mHeaderText.getText());
    592             Message mesg = mHandler.obtainMessage(MSG_SHOW_ERROR, next);
    593             mHandler.removeMessages(MSG_SHOW_ERROR);
    594             mHandler.sendMessageDelayed(mesg, ERROR_MESSAGE_TIMEOUT);
    595         }
    596 
    597         public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
    598             // Check if this was the result of hitting the enter or "done" key
    599             if (actionId == EditorInfo.IME_NULL
    600                     || actionId == EditorInfo.IME_ACTION_DONE
    601                     || actionId == EditorInfo.IME_ACTION_NEXT) {
    602                 handleNext();
    603                 return true;
    604             }
    605             return false;
    606         }
    607 
    608         /**
    609          * Update the hint based on current Stage and length of password entry
    610          */
    611         private void updateUi() {
    612             String password = mPasswordEntry.getText().toString();
    613             final int length = password.length();
    614             if (mUiStage == Stage.Introduction) {
    615                 if (length < mPasswordMinLength) {
    616                     String msg = getString(mIsAlphaMode ? R.string.lockpassword_password_too_short
    617                             : R.string.lockpassword_pin_too_short, mPasswordMinLength);
    618                     mHeaderText.setText(msg);
    619                     setNextEnabled(false);
    620                 } else {
    621                     String error = validatePassword(password);
    622                     if (error != null) {
    623                         mHeaderText.setText(error);
    624                         setNextEnabled(false);
    625                     } else {
    626                         mHeaderText.setText(R.string.lockpassword_press_continue);
    627                         setNextEnabled(true);
    628                     }
    629                 }
    630             } else {
    631                 mHeaderText.setText(mIsAlphaMode ? mUiStage.alphaHint : mUiStage.numericHint);
    632                 setNextEnabled(length > 0);
    633             }
    634             setNextText(mUiStage.buttonText);
    635         }
    636 
    637         public void afterTextChanged(Editable s) {
    638             // Changing the text while error displayed resets to NeedToConfirm state
    639             if (mUiStage == Stage.ConfirmWrong) {
    640                 mUiStage = Stage.NeedToConfirm;
    641             }
    642             updateUi();
    643         }
    644 
    645         public void beforeTextChanged(CharSequence s, int start, int count, int after) {
    646 
    647         }
    648 
    649         public void onTextChanged(CharSequence s, int start, int before, int count) {
    650 
    651         }
    652     }
    653 }
    654