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.password;
18 
19 import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_NONE;
20 import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_NUMERIC;
21 
22 import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_NONE;
23 import static com.android.internal.widget.PasswordValidationError.CONTAINS_INVALID_CHARACTERS;
24 import static com.android.internal.widget.PasswordValidationError.CONTAINS_SEQUENCE;
25 import static com.android.internal.widget.PasswordValidationError.NOT_ENOUGH_DIGITS;
26 import static com.android.internal.widget.PasswordValidationError.NOT_ENOUGH_LETTERS;
27 import static com.android.internal.widget.PasswordValidationError.NOT_ENOUGH_LOWER_CASE;
28 import static com.android.internal.widget.PasswordValidationError.NOT_ENOUGH_NON_DIGITS;
29 import static com.android.internal.widget.PasswordValidationError.NOT_ENOUGH_NON_LETTER;
30 import static com.android.internal.widget.PasswordValidationError.NOT_ENOUGH_SYMBOLS;
31 import static com.android.internal.widget.PasswordValidationError.NOT_ENOUGH_UPPER_CASE;
32 import static com.android.internal.widget.PasswordValidationError.RECENTLY_USED;
33 import static com.android.internal.widget.PasswordValidationError.TOO_LONG;
34 import static com.android.internal.widget.PasswordValidationError.TOO_SHORT;
35 import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_UNIFICATION_PROFILE_CREDENTIAL;
36 import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_UNIFICATION_PROFILE_ID;
37 
38 import android.app.Activity;
39 import android.app.admin.DevicePolicyManager;
40 import android.app.admin.DevicePolicyManager.PasswordComplexity;
41 import android.app.admin.PasswordMetrics;
42 import android.app.settings.SettingsEnums;
43 import android.content.Context;
44 import android.content.Intent;
45 import android.graphics.Insets;
46 import android.graphics.Typeface;
47 import android.os.Bundle;
48 import android.os.Handler;
49 import android.os.Message;
50 import android.os.UserHandle;
51 import android.os.UserManager;
52 import android.text.Editable;
53 import android.text.InputType;
54 import android.text.Selection;
55 import android.text.Spannable;
56 import android.text.TextUtils;
57 import android.text.TextWatcher;
58 import android.util.Log;
59 import android.util.Pair;
60 import android.view.KeyEvent;
61 import android.view.LayoutInflater;
62 import android.view.View;
63 import android.view.ViewGroup;
64 import android.view.inputmethod.EditorInfo;
65 import android.widget.ImeAwareEditText;
66 import android.widget.TextView;
67 import android.widget.TextView.OnEditorActionListener;
68 
69 import androidx.annotation.StringRes;
70 import androidx.fragment.app.Fragment;
71 import androidx.recyclerview.widget.LinearLayoutManager;
72 import androidx.recyclerview.widget.RecyclerView;
73 
74 import com.android.internal.annotations.VisibleForTesting;
75 import com.android.internal.widget.LockPatternUtils;
76 import com.android.internal.widget.LockscreenCredential;
77 import com.android.internal.widget.PasswordValidationError;
78 import com.android.internal.widget.TextViewInputDisabler;
79 import com.android.internal.widget.VerifyCredentialResponse;
80 import com.android.settings.EncryptionInterstitial;
81 import com.android.settings.R;
82 import com.android.settings.SettingsActivity;
83 import com.android.settings.SetupWizardUtils;
84 import com.android.settings.Utils;
85 import com.android.settings.core.InstrumentedFragment;
86 import com.android.settings.notification.RedactionInterstitial;
87 
88 import com.google.android.setupcompat.template.FooterBarMixin;
89 import com.google.android.setupcompat.template.FooterButton;
90 import com.google.android.setupdesign.GlifLayout;
91 import com.google.android.setupdesign.util.ThemeHelper;
92 
93 import java.util.ArrayList;
94 import java.util.Collections;
95 import java.util.List;
96 
97 public class ChooseLockPassword extends SettingsActivity {
98     private static final String TAG = "ChooseLockPassword";
99 
100     static final String EXTRA_KEY_MIN_METRICS = "min_metrics";
101     static final String EXTRA_KEY_MIN_COMPLEXITY = "min_complexity";
102 
103     @Override
getIntent()104     public Intent getIntent() {
105         Intent modIntent = new Intent(super.getIntent());
106         modIntent.putExtra(EXTRA_SHOW_FRAGMENT, getFragmentClass().getName());
107         return modIntent;
108     }
109 
110     public static class IntentBuilder {
111 
112         private final Intent mIntent;
113 
IntentBuilder(Context context)114         public IntentBuilder(Context context) {
115             mIntent = new Intent(context, ChooseLockPassword.class);
116             mIntent.putExtra(ChooseLockGeneric.CONFIRM_CREDENTIALS, false);
117             mIntent.putExtra(EncryptionInterstitial.EXTRA_REQUIRE_PASSWORD, false);
118         }
119 
120         /**
121          * Sets the intended credential type i.e. whether it's numeric PIN or general password
122          * @param passwordType password type represented by one of the {@code PASSWORD_QUALITY_}
123          *   constants.
124          */
setPasswordType(int passwordType)125         public IntentBuilder setPasswordType(int passwordType) {
126             mIntent.putExtra(LockPatternUtils.PASSWORD_TYPE_KEY, passwordType);
127             return this;
128         }
129 
setUserId(int userId)130         public IntentBuilder setUserId(int userId) {
131             mIntent.putExtra(Intent.EXTRA_USER_ID, userId);
132             return this;
133         }
134 
setRequestGatekeeperPasswordHandle( boolean requestGatekeeperPasswordHandle)135         public IntentBuilder setRequestGatekeeperPasswordHandle(
136                 boolean requestGatekeeperPasswordHandle) {
137             mIntent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_REQUEST_GK_PW_HANDLE,
138                     requestGatekeeperPasswordHandle);
139             return this;
140         }
141 
setPassword(LockscreenCredential password)142         public IntentBuilder setPassword(LockscreenCredential password) {
143             mIntent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD, password);
144             return this;
145         }
146 
setForFingerprint(boolean forFingerprint)147         public IntentBuilder setForFingerprint(boolean forFingerprint) {
148             mIntent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_FOR_FINGERPRINT, forFingerprint);
149             return this;
150         }
151 
setForFace(boolean forFace)152         public IntentBuilder setForFace(boolean forFace) {
153             mIntent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_FOR_FACE, forFace);
154             return this;
155         }
156 
setForBiometrics(boolean forBiometrics)157         public IntentBuilder setForBiometrics(boolean forBiometrics) {
158             mIntent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_FOR_BIOMETRICS, forBiometrics);
159             return this;
160         }
161 
162         /** Sets the minimum password requirement in terms of complexity and metrics */
setPasswordRequirement(@asswordComplexity int level, PasswordMetrics metrics)163         public IntentBuilder setPasswordRequirement(@PasswordComplexity int level,
164                 PasswordMetrics metrics) {
165             mIntent.putExtra(EXTRA_KEY_MIN_COMPLEXITY, level);
166             mIntent.putExtra(EXTRA_KEY_MIN_METRICS, metrics);
167             return this;
168         }
169 
170         /**
171          * Configures the launch such that at the end of the password enrollment, one of its
172          * managed profile (specified by {@code profileId}) will have its lockscreen unified
173          * to the parent user. The profile's current lockscreen credential needs to be specified by
174          * {@code credential}.
175          */
setProfileToUnify(int profileId, LockscreenCredential credential)176         public IntentBuilder setProfileToUnify(int profileId, LockscreenCredential credential) {
177             mIntent.putExtra(EXTRA_KEY_UNIFICATION_PROFILE_ID, profileId);
178             mIntent.putExtra(EXTRA_KEY_UNIFICATION_PROFILE_CREDENTIAL, credential);
179             return this;
180         }
181 
build()182         public Intent build() {
183             return mIntent;
184         }
185     }
186 
187     @Override
isValidFragment(String fragmentName)188     protected boolean isValidFragment(String fragmentName) {
189         if (ChooseLockPasswordFragment.class.getName().equals(fragmentName)) return true;
190         return false;
191     }
192 
193     @Override
isToolbarEnabled()194     protected boolean isToolbarEnabled() {
195         return false;
196     }
197 
getFragmentClass()198     /* package */ Class<? extends Fragment> getFragmentClass() {
199         return ChooseLockPasswordFragment.class;
200     }
201 
202     @Override
onCreate(Bundle savedInstanceState)203     protected void onCreate(Bundle savedInstanceState) {
204         setTheme(SetupWizardUtils.getTheme(this, getIntent()));
205         ThemeHelper.trySetDynamicColor(this);
206         super.onCreate(savedInstanceState);
207         findViewById(R.id.content_parent).setFitsSystemWindows(false);
208     }
209 
210     public static class ChooseLockPasswordFragment extends InstrumentedFragment
211             implements OnEditorActionListener, TextWatcher, SaveAndFinishWorker.Listener {
212         private static final String KEY_FIRST_PASSWORD = "first_password";
213         private static final String KEY_UI_STAGE = "ui_stage";
214         private static final String KEY_CURRENT_CREDENTIAL = "current_credential";
215         private static final String FRAGMENT_TAG_SAVE_AND_FINISH = "save_and_finish_worker";
216 
217         private LockscreenCredential mCurrentCredential;
218         private LockscreenCredential mChosenPassword;
219         private boolean mRequestGatekeeperPassword;
220         private ImeAwareEditText mPasswordEntry;
221         private TextViewInputDisabler mPasswordEntryInputDisabler;
222 
223         // Minimum password metrics enforced by admins.
224         private PasswordMetrics mMinMetrics;
225         private List<PasswordValidationError> mValidationErrors;
226 
227         @PasswordComplexity private int mMinComplexity = PASSWORD_COMPLEXITY_NONE;
228         protected int mUserId;
229         private byte[] mPasswordHistoryHashFactor;
230         private int mUnificationProfileId = UserHandle.USER_NULL;
231 
232         private LockPatternUtils mLockPatternUtils;
233         private SaveAndFinishWorker mSaveAndFinishWorker;
234         private int mPasswordType = DevicePolicyManager.PASSWORD_QUALITY_NUMERIC;
235         protected Stage mUiStage = Stage.Introduction;
236         private PasswordRequirementAdapter mPasswordRequirementAdapter;
237         private GlifLayout mLayout;
238         protected boolean mForFingerprint;
239         protected boolean mForFace;
240         protected boolean mForBiometrics;
241 
242         private LockscreenCredential mFirstPassword;
243         private RecyclerView mPasswordRestrictionView;
244         protected boolean mIsAlphaMode;
245         protected boolean mIsManagedProfile;
246         protected FooterButton mSkipOrClearButton;
247         private FooterButton mNextButton;
248         private TextView mMessage;
249 
250         private TextChangedHandler mTextChangedHandler;
251 
252         private static final int CONFIRM_EXISTING_REQUEST = 58;
253         static final int RESULT_FINISHED = RESULT_FIRST_USER;
254 
255         /**
256          * Keep track internally of where the user is in choosing a pattern.
257          */
258         protected enum Stage {
259 
260             Introduction(
261                     R.string.lockpassword_choose_your_password_header, // password
262                     R.string.lockpassword_choose_your_profile_password_header,
263                     R.string.lockpassword_choose_your_password_header_for_fingerprint,
264                     R.string.lockpassword_choose_your_password_header_for_face,
265                     R.string.lockpassword_choose_your_password_header_for_biometrics,
266                     R.string.lockpassword_choose_your_pin_header, // pin
267                     R.string.lockpassword_choose_your_profile_pin_header,
268                     R.string.lockpassword_choose_your_pin_header_for_fingerprint,
269                     R.string.lockpassword_choose_your_pin_header_for_face,
270                     R.string.lockpassword_choose_your_pin_header_for_biometrics,
271                     R.string.lockpassword_choose_password_description,
272                     R.string.lock_settings_picker_biometrics_added_security_message,
273                     R.string.lockpassword_choose_pin_description,
274                     R.string.lock_settings_picker_biometrics_added_security_message,
275                     R.string.next_label),
276 
277             NeedToConfirm(
278                     R.string.lockpassword_confirm_your_password_header,
279                     R.string.lockpassword_reenter_your_profile_password_header,
280                     R.string.lockpassword_confirm_your_password_header,
281                     R.string.lockpassword_confirm_your_password_header,
282                     R.string.lockpassword_confirm_your_password_header,
283                     R.string.lockpassword_confirm_your_pin_header,
284                     R.string.lockpassword_reenter_your_profile_pin_header,
285                     R.string.lockpassword_confirm_your_pin_header,
286                     R.string.lockpassword_confirm_your_pin_header,
287                     R.string.lockpassword_confirm_your_pin_header,
288                     0,
289                     0,
290                     0,
291                     0,
292                     R.string.lockpassword_confirm_label),
293 
294             ConfirmWrong(
295                     R.string.lockpassword_confirm_passwords_dont_match,
296                     R.string.lockpassword_confirm_passwords_dont_match,
297                     R.string.lockpassword_confirm_passwords_dont_match,
298                     R.string.lockpassword_confirm_passwords_dont_match,
299                     R.string.lockpassword_confirm_passwords_dont_match,
300                     R.string.lockpassword_confirm_pins_dont_match,
301                     R.string.lockpassword_confirm_pins_dont_match,
302                     R.string.lockpassword_confirm_pins_dont_match,
303                     R.string.lockpassword_confirm_pins_dont_match,
304                     R.string.lockpassword_confirm_pins_dont_match,
305                     0,
306                     0,
307                     0,
308                     0,
309                     R.string.lockpassword_confirm_label);
310 
Stage(int hintInAlpha, int hintInAlphaForProfile, int hintInAlphaForFingerprint, int hintInAlphaForFace, int hintInAlphaForBiometrics, int hintInNumeric, int hintInNumericForProfile, int hintInNumericForFingerprint, int hintInNumericForFace, int hintInNumericForBiometrics, int messageInAlpha, int messageInAlphaForBiometrics, int messageInNumeric, int messageInNumericForBiometrics, int nextButtonText)311             Stage(int hintInAlpha,
312                     int hintInAlphaForProfile,
313                     int hintInAlphaForFingerprint,
314                     int hintInAlphaForFace,
315                     int hintInAlphaForBiometrics,
316                     int hintInNumeric,
317                     int hintInNumericForProfile,
318                     int hintInNumericForFingerprint,
319                     int hintInNumericForFace,
320                     int hintInNumericForBiometrics,
321                     int messageInAlpha,
322                     int messageInAlphaForBiometrics,
323                     int messageInNumeric,
324                     int messageInNumericForBiometrics,
325                     int nextButtonText) {
326 
327                 this.alphaHint = hintInAlpha;
328                 this.alphaHintForProfile = hintInAlphaForProfile;
329                 this.alphaHintForFingerprint = hintInAlphaForFingerprint;
330                 this.alphaHintForFace = hintInAlphaForFace;
331                 this.alphaHintForBiometrics = hintInAlphaForBiometrics;
332 
333                 this.numericHint = hintInNumeric;
334                 this.numericHintForProfile = hintInNumericForProfile;
335                 this.numericHintForFingerprint = hintInNumericForFingerprint;
336                 this.numericHintForFace = hintInNumericForFace;
337                 this.numericHintForBiometrics = hintInNumericForBiometrics;
338 
339                 this.alphaMessage = messageInAlpha;
340                 this.alphaMessageForBiometrics = messageInAlphaForBiometrics;
341 
342                 this.numericMessage = messageInNumeric;
343                 this.numericMessageForBiometrics = messageInNumericForBiometrics;
344 
345                 this.buttonText = nextButtonText;
346             }
347 
348             public static final int TYPE_NONE = 0;
349             public static final int TYPE_FINGERPRINT = 1;
350             public static final int TYPE_FACE = 2;
351             public static final int TYPE_BIOMETRIC = 3;
352 
353             // Password header
354             public final int alphaHint;
355             public final int alphaHintForProfile;
356             public final int alphaHintForFingerprint;
357             public final int alphaHintForFace;
358             public final int alphaHintForBiometrics;
359 
360             // PIN header
361             public final int numericHint;
362             public final int numericHintForProfile;
363             public final int numericHintForFingerprint;
364             public final int numericHintForFace;
365             public final int numericHintForBiometrics;
366 
367             // Password description
368             public final int alphaMessage;
369             public final int alphaMessageForBiometrics;
370 
371             // PIN description
372             public final int numericMessage;
373             public final int numericMessageForBiometrics;
374 
375             public final int buttonText;
376 
getHint(boolean isAlpha, int type, boolean isProfile)377             public @StringRes int getHint(boolean isAlpha, int type, boolean isProfile) {
378                 if (isAlpha) {
379                     if (type == TYPE_FINGERPRINT) {
380                         return alphaHintForFingerprint;
381                     } else if (type == TYPE_FACE) {
382                         return alphaHintForFace;
383                     } else if (type == TYPE_BIOMETRIC) {
384                         return alphaHintForBiometrics;
385                     } else {
386                         return isProfile ? alphaHintForProfile : alphaHint;
387                     }
388                 } else {
389                     if (type == TYPE_FINGERPRINT) {
390                         return numericHintForFingerprint;
391                     } else if (type == TYPE_FACE) {
392                         return numericHintForFace;
393                     } else if (type == TYPE_BIOMETRIC) {
394                         return numericHintForBiometrics;
395                     } else {
396                         return isProfile ? numericHintForProfile : numericHint;
397                     }
398                 }
399             }
400 
getMessage(boolean isAlpha, int type)401             public @StringRes int getMessage(boolean isAlpha, int type) {
402                 switch (type) {
403                     case TYPE_FINGERPRINT:
404                     case TYPE_FACE:
405                     case TYPE_BIOMETRIC:
406                         return isAlpha ? alphaMessageForBiometrics : numericMessageForBiometrics;
407 
408                     case TYPE_NONE:
409                     default:
410                         return isAlpha ? alphaMessage : numericMessage;
411                 }
412             }
413         }
414 
415         // required constructor for fragments
ChooseLockPasswordFragment()416         public ChooseLockPasswordFragment() {
417 
418         }
419 
420         @Override
onCreate(Bundle savedInstanceState)421         public void onCreate(Bundle savedInstanceState) {
422             super.onCreate(savedInstanceState);
423             mLockPatternUtils = new LockPatternUtils(getActivity());
424             Intent intent = getActivity().getIntent();
425             if (!(getActivity() instanceof ChooseLockPassword)) {
426                 throw new SecurityException("Fragment contained in wrong activity");
427             }
428             // Only take this argument into account if it belongs to the current profile.
429             mUserId = Utils.getUserIdFromBundle(getActivity(), intent.getExtras());
430             mIsManagedProfile = UserManager.get(getActivity()).isManagedProfile(mUserId);
431             mForFingerprint = intent.getBooleanExtra(
432                     ChooseLockSettingsHelper.EXTRA_KEY_FOR_FINGERPRINT, false);
433             mForFace = intent.getBooleanExtra(ChooseLockSettingsHelper.EXTRA_KEY_FOR_FACE, false);
434             mForBiometrics = intent.getBooleanExtra(
435                     ChooseLockSettingsHelper.EXTRA_KEY_FOR_BIOMETRICS, false);
436 
437             mPasswordType = intent.getIntExtra(
438                     LockPatternUtils.PASSWORD_TYPE_KEY, PASSWORD_QUALITY_NUMERIC);
439             mUnificationProfileId = intent.getIntExtra(
440                     EXTRA_KEY_UNIFICATION_PROFILE_ID, UserHandle.USER_NULL);
441 
442             mMinComplexity = intent.getIntExtra(EXTRA_KEY_MIN_COMPLEXITY, PASSWORD_COMPLEXITY_NONE);
443             mMinMetrics = intent.getParcelableExtra(EXTRA_KEY_MIN_METRICS);
444             if (mMinMetrics == null) mMinMetrics = new PasswordMetrics(CREDENTIAL_TYPE_NONE);
445 
446             if (intent.getBooleanExtra(
447                     ChooseLockSettingsHelper.EXTRA_KEY_FOR_CHANGE_CRED_REQUIRED_FOR_BOOT, false)) {
448                 SaveAndFinishWorker w = new SaveAndFinishWorker();
449                 final boolean required = getActivity().getIntent().getBooleanExtra(
450                         EncryptionInterstitial.EXTRA_REQUIRE_PASSWORD, true);
451                 LockscreenCredential currentCredential = intent.getParcelableExtra(
452                         ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD);
453 
454                 final LockPatternUtils utils = new LockPatternUtils(getActivity());
455 
456                 w.setBlocking(true);
457                 w.setListener(this);
458                 w.start(utils, required, false /* requestGatekeeperPassword */, currentCredential,
459                         currentCredential, mUserId);
460             }
461             mTextChangedHandler = new TextChangedHandler();
462         }
463 
464         @Override
onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)465         public View onCreateView(LayoutInflater inflater, ViewGroup container,
466                 Bundle savedInstanceState) {
467             return inflater.inflate(R.layout.choose_lock_password, container, false);
468         }
469 
470         @Override
onViewCreated(View view, Bundle savedInstanceState)471         public void onViewCreated(View view, Bundle savedInstanceState) {
472             super.onViewCreated(view, savedInstanceState);
473 
474             mLayout = (GlifLayout) view;
475 
476             // Make the password container consume the optical insets so the edit text is aligned
477             // with the sides of the parent visually.
478             ViewGroup container = view.findViewById(R.id.password_container);
479             container.setOpticalInsets(Insets.NONE);
480 
481             final FooterBarMixin mixin = mLayout.getMixin(FooterBarMixin.class);
482             mixin.setSecondaryButton(
483                     new FooterButton.Builder(getActivity())
484                             .setText(R.string.lockpassword_clear_label)
485                             .setListener(this::onSkipOrClearButtonClick)
486                             .setButtonType(FooterButton.ButtonType.SKIP)
487                             .setTheme(R.style.SudGlifButton_Secondary)
488                             .build()
489             );
490             mixin.setPrimaryButton(
491                     new FooterButton.Builder(getActivity())
492                             .setText(R.string.next_label)
493                             .setListener(this::onNextButtonClick)
494                             .setButtonType(FooterButton.ButtonType.NEXT)
495                             .setTheme(R.style.SudGlifButton_Primary)
496                             .build()
497             );
498             mSkipOrClearButton = mixin.getSecondaryButton();
499             mNextButton = mixin.getPrimaryButton();
500 
501             mMessage = view.findViewById(R.id.sud_layout_description);
502             mLayout.setIcon(getActivity().getDrawable(R.drawable.ic_lock));
503 
504             mIsAlphaMode = DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC == mPasswordType
505                     || DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC == mPasswordType
506                     || DevicePolicyManager.PASSWORD_QUALITY_COMPLEX == mPasswordType;
507 
508             setupPasswordRequirementsView(view);
509 
510             mPasswordRestrictionView.setLayoutManager(new LinearLayoutManager(getActivity()));
511             mPasswordEntry = view.findViewById(R.id.password_entry);
512             mPasswordEntry.setOnEditorActionListener(this);
513             mPasswordEntry.addTextChangedListener(this);
514             mPasswordEntry.requestFocus();
515             mPasswordEntryInputDisabler = new TextViewInputDisabler(mPasswordEntry);
516 
517             final Activity activity = getActivity();
518 
519             int currentType = mPasswordEntry.getInputType();
520             mPasswordEntry.setInputType(mIsAlphaMode ? currentType
521                     : (InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_VARIATION_PASSWORD));
522             if (mIsAlphaMode) {
523                 mPasswordEntry.setContentDescription(
524                         getString(R.string.unlock_set_unlock_password_title));
525             } else {
526                 mPasswordEntry.setContentDescription(
527                         getString(R.string.unlock_set_unlock_pin_title));
528             }
529             // Can't set via XML since setInputType resets the fontFamily to null
530             mPasswordEntry.setTypeface(Typeface.create(
531                     getContext().getString(com.android.internal.R.string.config_headlineFontFamily),
532                     Typeface.NORMAL));
533 
534             Intent intent = getActivity().getIntent();
535             final boolean confirmCredentials = intent.getBooleanExtra(
536                     ChooseLockGeneric.CONFIRM_CREDENTIALS, true);
537             mCurrentCredential = intent.getParcelableExtra(
538                     ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD);
539             mRequestGatekeeperPassword = intent.getBooleanExtra(
540                     ChooseLockSettingsHelper.EXTRA_KEY_REQUEST_GK_PW_HANDLE, false);
541             if (savedInstanceState == null) {
542                 updateStage(Stage.Introduction);
543                 if (confirmCredentials) {
544                     final ChooseLockSettingsHelper.Builder builder =
545                             new ChooseLockSettingsHelper.Builder(getActivity());
546                     builder.setRequestCode(CONFIRM_EXISTING_REQUEST)
547                             .setTitle(getString(R.string.unlock_set_unlock_launch_picker_title))
548                             .setReturnCredentials(true)
549                             .setRequestGatekeeperPasswordHandle(mRequestGatekeeperPassword)
550                             .setUserId(mUserId)
551                             .show();
552                 }
553             } else {
554 
555                 // restore from previous state
556                 mFirstPassword = savedInstanceState.getParcelable(KEY_FIRST_PASSWORD);
557                 final String state = savedInstanceState.getString(KEY_UI_STAGE);
558                 if (state != null) {
559                     mUiStage = Stage.valueOf(state);
560                     updateStage(mUiStage);
561                 }
562 
563                 mCurrentCredential = savedInstanceState.getParcelable(KEY_CURRENT_CREDENTIAL);
564 
565                 // Re-attach to the exiting worker if there is one.
566                 mSaveAndFinishWorker = (SaveAndFinishWorker) getFragmentManager().findFragmentByTag(
567                         FRAGMENT_TAG_SAVE_AND_FINISH);
568             }
569 
570             if (activity instanceof SettingsActivity) {
571                 final SettingsActivity sa = (SettingsActivity) activity;
572                 int title = Stage.Introduction.getHint(mIsAlphaMode, getStageType(),
573                         mIsManagedProfile);
574                 sa.setTitle(title);
575                 mLayout.setHeaderText(title);
576             }
577         }
578 
579         @Override
onDestroy()580         public void onDestroy() {
581             super.onDestroy();
582             if (mCurrentCredential != null) {
583                 mCurrentCredential.zeroize();
584             }
585             // Force a garbage collection immediately to remove remnant of user password shards
586             // from memory.
587             System.gc();
588             System.runFinalization();
589             System.gc();
590         }
591 
getStageType()592         protected int getStageType() {
593             if (mForFingerprint) {
594                 return Stage.TYPE_FINGERPRINT;
595             } else if (mForFace) {
596                 return Stage.TYPE_FACE;
597             } else if (mForBiometrics) {
598                 return Stage.TYPE_BIOMETRIC;
599             } else {
600                 return Stage.TYPE_NONE;
601             }
602         }
603 
setupPasswordRequirementsView(View view)604         private void setupPasswordRequirementsView(View view) {
605             mPasswordRestrictionView = view.findViewById(R.id.password_requirements_view);
606             mPasswordRestrictionView.setLayoutManager(new LinearLayoutManager(getActivity()));
607             mPasswordRequirementAdapter = new PasswordRequirementAdapter();
608             mPasswordRestrictionView.setAdapter(mPasswordRequirementAdapter);
609         }
610 
611         @Override
getMetricsCategory()612         public int getMetricsCategory() {
613             return SettingsEnums.CHOOSE_LOCK_PASSWORD;
614         }
615 
616         @Override
onResume()617         public void onResume() {
618             super.onResume();
619             updateStage(mUiStage);
620             if (mSaveAndFinishWorker != null) {
621                 mSaveAndFinishWorker.setListener(this);
622             } else {
623                 mPasswordEntry.requestFocus();
624                 mPasswordEntry.scheduleShowSoftInput();
625             }
626         }
627 
628         @Override
onPause()629         public void onPause() {
630             if (mSaveAndFinishWorker != null) {
631                 mSaveAndFinishWorker.setListener(null);
632             }
633             super.onPause();
634         }
635 
636         @Override
onSaveInstanceState(Bundle outState)637         public void onSaveInstanceState(Bundle outState) {
638             super.onSaveInstanceState(outState);
639             outState.putString(KEY_UI_STAGE, mUiStage.name());
640             outState.putParcelable(KEY_FIRST_PASSWORD, mFirstPassword);
641             if (mCurrentCredential != null) {
642                 outState.putParcelable(KEY_CURRENT_CREDENTIAL, mCurrentCredential.duplicate());
643             }
644         }
645 
646         @Override
onActivityResult(int requestCode, int resultCode, Intent data)647         public void onActivityResult(int requestCode, int resultCode,
648                 Intent data) {
649             super.onActivityResult(requestCode, resultCode, data);
650             switch (requestCode) {
651                 case CONFIRM_EXISTING_REQUEST:
652                     if (resultCode != Activity.RESULT_OK) {
653                         getActivity().setResult(RESULT_FINISHED);
654                         getActivity().finish();
655                     } else {
656                         mCurrentCredential = data.getParcelableExtra(
657                                 ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD);
658                     }
659                     break;
660             }
661         }
662 
getRedactionInterstitialIntent(Context context)663         protected Intent getRedactionInterstitialIntent(Context context) {
664             return RedactionInterstitial.createStartIntent(context, mUserId);
665         }
666 
updateStage(Stage stage)667         protected void updateStage(Stage stage) {
668             final Stage previousStage = mUiStage;
669             mUiStage = stage;
670             updateUi();
671 
672             // If the stage changed, announce the header for accessibility. This
673             // is a no-op when accessibility is disabled.
674             if (previousStage != stage) {
675                 mLayout.announceForAccessibility(mLayout.getHeaderText());
676             }
677         }
678 
679         /**
680          * Validates PIN/Password and returns the validation result and updates mValidationErrors
681          * and mPasswordReused to reflect validation results.
682          *
683          * @param credential credential the user typed in.
684          * @return whether password satisfies all the requirements.
685          */
686         @VisibleForTesting
validatePassword(LockscreenCredential credential)687         boolean validatePassword(LockscreenCredential credential) {
688             final byte[] password = credential.getCredential();
689             mValidationErrors = PasswordMetrics.validatePassword(
690                     mMinMetrics, mMinComplexity, !mIsAlphaMode, password);
691             if (mValidationErrors.isEmpty() &&  mLockPatternUtils.checkPasswordHistory(
692                         password, getPasswordHistoryHashFactor(), mUserId)) {
693                 mValidationErrors =
694                         Collections.singletonList(new PasswordValidationError(RECENTLY_USED));
695             }
696             return mValidationErrors.isEmpty();
697         }
698 
699         /**
700          * Lazily compute and return the history hash factor of the current user (mUserId), used for
701          * password history check.
702          */
getPasswordHistoryHashFactor()703         private byte[] getPasswordHistoryHashFactor() {
704             if (mPasswordHistoryHashFactor == null) {
705                 mPasswordHistoryHashFactor = mLockPatternUtils.getPasswordHistoryHashFactor(
706                         mCurrentCredential != null ? mCurrentCredential
707                                 : LockscreenCredential.createNone(), mUserId);
708             }
709             return mPasswordHistoryHashFactor;
710         }
711 
handleNext()712         public void handleNext() {
713             if (mSaveAndFinishWorker != null) return;
714             // TODO(b/120484642): This is a point of entry for passwords from the UI
715             final Editable passwordText = mPasswordEntry.getText();
716             if (TextUtils.isEmpty(passwordText)) {
717                 return;
718             }
719             mChosenPassword = mIsAlphaMode ? LockscreenCredential.createPassword(passwordText)
720                     : LockscreenCredential.createPin(passwordText);
721             if (mUiStage == Stage.Introduction) {
722                 if (validatePassword(mChosenPassword)) {
723                     mFirstPassword = mChosenPassword;
724                     mPasswordEntry.setText("");
725                     updateStage(Stage.NeedToConfirm);
726                 } else {
727                     mChosenPassword.zeroize();
728                 }
729             } else if (mUiStage == Stage.NeedToConfirm) {
730                 if (mChosenPassword.equals(mFirstPassword)) {
731                     startSaveAndFinish();
732                 } else {
733                     CharSequence tmp = mPasswordEntry.getText();
734                     if (tmp != null) {
735                         Selection.setSelection((Spannable) tmp, 0, tmp.length());
736                     }
737                     updateStage(Stage.ConfirmWrong);
738                     mChosenPassword.zeroize();
739                 }
740             }
741         }
742 
setNextEnabled(boolean enabled)743         protected void setNextEnabled(boolean enabled) {
744             mNextButton.setEnabled(enabled);
745         }
746 
setNextText(int text)747         protected void setNextText(int text) {
748             mNextButton.setText(getActivity(), text);
749         }
750 
onSkipOrClearButtonClick(View view)751         protected void onSkipOrClearButtonClick(View view) {
752             mPasswordEntry.setText("");
753         }
754 
onNextButtonClick(View view)755         protected void onNextButtonClick(View view) {
756             handleNext();
757         }
758 
onEditorAction(TextView v, int actionId, KeyEvent event)759         public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
760             // Check if this was the result of hitting the enter or "done" key
761             if (actionId == EditorInfo.IME_NULL
762                     || actionId == EditorInfo.IME_ACTION_DONE
763                     || actionId == EditorInfo.IME_ACTION_NEXT) {
764                 handleNext();
765                 return true;
766             }
767             return false;
768         }
769 
770         /**
771          * @param errorCode error code returned from password validation.
772          * @return an array of messages describing the error, important messages come first.
773          */
convertErrorCodeToMessages()774         String[] convertErrorCodeToMessages() {
775             List<String> messages = new ArrayList<>();
776             for (PasswordValidationError error : mValidationErrors) {
777                 switch (error.errorCode) {
778                     case CONTAINS_INVALID_CHARACTERS:
779                         messages.add(getString(R.string.lockpassword_illegal_character));
780                         break;
781                     case NOT_ENOUGH_UPPER_CASE:
782                         messages.add(getResources().getQuantityString(
783                                 R.plurals.lockpassword_password_requires_uppercase,
784                                 error.requirement, error.requirement));
785                         break;
786                     case NOT_ENOUGH_LOWER_CASE:
787                         messages.add(getResources().getQuantityString(
788                                 R.plurals.lockpassword_password_requires_lowercase,
789                                 error.requirement, error.requirement));
790                         break;
791                     case NOT_ENOUGH_LETTERS:
792                         messages.add(getResources().getQuantityString(
793                                 R.plurals.lockpassword_password_requires_letters,
794                                 error.requirement, error.requirement));
795                         break;
796                     case NOT_ENOUGH_DIGITS:
797                         messages.add(getResources().getQuantityString(
798                                 R.plurals.lockpassword_password_requires_numeric,
799                                 error.requirement, error.requirement));
800                         break;
801                     case NOT_ENOUGH_SYMBOLS:
802                         messages.add(getResources().getQuantityString(
803                                 R.plurals.lockpassword_password_requires_symbols,
804                                 error.requirement, error.requirement));
805                         break;
806                     case NOT_ENOUGH_NON_LETTER:
807                         messages.add(getResources().getQuantityString(
808                                 R.plurals.lockpassword_password_requires_nonletter,
809                                 error.requirement, error.requirement));
810                         break;
811                     case NOT_ENOUGH_NON_DIGITS:
812                         messages.add(getResources().getQuantityString(
813                                 R.plurals.lockpassword_password_requires_nonnumerical,
814                                 error.requirement, error.requirement));
815                         break;
816                     case TOO_SHORT:
817                         messages.add(getResources().getQuantityString(
818                                 mIsAlphaMode
819                                         ? R.plurals.lockpassword_password_too_short
820                                         : R.plurals.lockpassword_pin_too_short,
821                                 error.requirement, error.requirement));
822                         break;
823                     case TOO_LONG:
824                         messages.add(getResources().getQuantityString(
825                                 mIsAlphaMode
826                                         ? R.plurals.lockpassword_password_too_long
827                                         : R.plurals.lockpassword_pin_too_long,
828                                 error.requirement + 1, error.requirement + 1));
829                         break;
830                     case CONTAINS_SEQUENCE:
831                         messages.add(getString(R.string.lockpassword_pin_no_sequential_digits));
832                         break;
833                     case RECENTLY_USED:
834                         messages.add(getString(mIsAlphaMode
835                                 ? R.string.lockpassword_password_recently_used
836                                 : R.string.lockpassword_pin_recently_used));
837                         break;
838                     default:
839                         Log.wtf(TAG, "unknown error validating password: " + error);
840                 }
841             }
842 
843             return messages.toArray(new String[0]);
844         }
845 
846         /**
847          * Update the hint based on current Stage and length of password entry
848          */
updateUi()849         protected void updateUi() {
850             final boolean canInput = mSaveAndFinishWorker == null;
851 
852             LockscreenCredential password = mIsAlphaMode
853                     ? LockscreenCredential.createPasswordOrNone(mPasswordEntry.getText())
854                     : LockscreenCredential.createPinOrNone(mPasswordEntry.getText());
855             final int length = password.size();
856             if (mUiStage == Stage.Introduction) {
857                 mPasswordRestrictionView.setVisibility(View.VISIBLE);
858                 final boolean passwordCompliant = validatePassword(password);
859                 String[] messages = convertErrorCodeToMessages();
860                 // Update the fulfillment of requirements.
861                 mPasswordRequirementAdapter.setRequirements(messages);
862                 // Enable/Disable the next button accordingly.
863                 setNextEnabled(passwordCompliant);
864             } else {
865                 // Hide password requirement view when we are just asking user to confirm the pw.
866                 mPasswordRestrictionView.setVisibility(View.GONE);
867                 setHeaderText(getString(mUiStage.getHint(mIsAlphaMode, getStageType(),
868                         mIsManagedProfile)));
869                 setNextEnabled(canInput && length >= LockPatternUtils.MIN_LOCK_PASSWORD_SIZE);
870                 mSkipOrClearButton.setVisibility(toVisibility(canInput && length > 0));
871             }
872             int message = mUiStage.getMessage(mIsAlphaMode, getStageType());
873             if (message != 0) {
874                 mMessage.setVisibility(View.VISIBLE);
875                 mMessage.setText(message);
876             } else {
877                 mMessage.setVisibility(View.INVISIBLE);
878             }
879 
880             setNextText(mUiStage.buttonText);
881             mPasswordEntryInputDisabler.setInputEnabled(canInput);
882             password.zeroize();
883         }
884 
toVisibility(boolean visibleOrGone)885         protected int toVisibility(boolean visibleOrGone) {
886             return visibleOrGone ? View.VISIBLE : View.GONE;
887         }
888 
setHeaderText(String text)889         private void setHeaderText(String text) {
890             // Only set the text if it is different than the existing one to avoid announcing again.
891             if (!TextUtils.isEmpty(mLayout.getHeaderText())
892                     && mLayout.getHeaderText().toString().equals(text)) {
893                 return;
894             }
895             mLayout.setHeaderText(text);
896         }
897 
afterTextChanged(Editable s)898         public void afterTextChanged(Editable s) {
899             // Changing the text while error displayed resets to NeedToConfirm state
900             if (mUiStage == Stage.ConfirmWrong) {
901                 mUiStage = Stage.NeedToConfirm;
902             }
903             // Schedule the UI update.
904             mTextChangedHandler.notifyAfterTextChanged();
905         }
906 
beforeTextChanged(CharSequence s, int start, int count, int after)907         public void beforeTextChanged(CharSequence s, int start, int count, int after) {
908 
909         }
910 
onTextChanged(CharSequence s, int start, int before, int count)911         public void onTextChanged(CharSequence s, int start, int before, int count) {
912 
913         }
914 
startSaveAndFinish()915         private void startSaveAndFinish() {
916             if (mSaveAndFinishWorker != null) {
917                 Log.w(TAG, "startSaveAndFinish with an existing SaveAndFinishWorker.");
918                 return;
919             }
920 
921             mPasswordEntryInputDisabler.setInputEnabled(false);
922             setNextEnabled(false);
923 
924             mSaveAndFinishWorker = new SaveAndFinishWorker();
925             mSaveAndFinishWorker.setListener(this);
926 
927             getFragmentManager().beginTransaction().add(mSaveAndFinishWorker,
928                     FRAGMENT_TAG_SAVE_AND_FINISH).commit();
929             getFragmentManager().executePendingTransactions();
930 
931             final Intent intent = getActivity().getIntent();
932             final boolean required = intent.getBooleanExtra(
933                     EncryptionInterstitial.EXTRA_REQUIRE_PASSWORD, true);
934             if (mUnificationProfileId != UserHandle.USER_NULL) {
935                 try (LockscreenCredential profileCredential = (LockscreenCredential)
936                         intent.getParcelableExtra(EXTRA_KEY_UNIFICATION_PROFILE_CREDENTIAL)) {
937                     mSaveAndFinishWorker.setProfileToUnify(mUnificationProfileId,
938                             profileCredential);
939                 }
940             }
941             mSaveAndFinishWorker.start(mLockPatternUtils, required, mRequestGatekeeperPassword,
942                     mChosenPassword, mCurrentCredential, mUserId);
943         }
944 
945         @Override
onChosenLockSaveFinished(boolean wasSecureBefore, Intent resultData)946         public void onChosenLockSaveFinished(boolean wasSecureBefore, Intent resultData) {
947             getActivity().setResult(RESULT_FINISHED, resultData);
948 
949             if (mChosenPassword != null) {
950                 mChosenPassword.zeroize();
951             }
952             if (mCurrentCredential != null) {
953                 mCurrentCredential.zeroize();
954             }
955             if (mFirstPassword != null) {
956                 mFirstPassword.zeroize();
957             }
958 
959             mPasswordEntry.setText("");
960 
961             if (!wasSecureBefore) {
962                 Intent intent = getRedactionInterstitialIntent(getActivity());
963                 if (intent != null) {
964                     startActivity(intent);
965                 }
966             }
967             getActivity().finish();
968         }
969 
970         class TextChangedHandler extends Handler {
971             private static final int ON_TEXT_CHANGED = 1;
972             private static final int DELAY_IN_MILLISECOND = 100;
973 
974             /**
975              * With the introduction of delay, we batch processing the text changed event to reduce
976              * unnecessary UI updates.
977              */
notifyAfterTextChanged()978             private void notifyAfterTextChanged() {
979                 removeMessages(ON_TEXT_CHANGED);
980                 sendEmptyMessageDelayed(ON_TEXT_CHANGED, DELAY_IN_MILLISECOND);
981             }
982 
983             @Override
handleMessage(Message msg)984             public void handleMessage(Message msg) {
985                 if (getActivity() == null) {
986                     return;
987                 }
988                 if (msg.what == ON_TEXT_CHANGED) {
989                     updateUi();
990                 }
991             }
992         }
993     }
994 
995     public static class SaveAndFinishWorker extends SaveChosenLockWorkerBase {
996 
997         private LockscreenCredential mChosenPassword;
998         private LockscreenCredential mCurrentCredential;
999 
start(LockPatternUtils utils, boolean required, boolean requestGatekeeperPassword, LockscreenCredential chosenPassword, LockscreenCredential currentCredential, int userId)1000         public void start(LockPatternUtils utils, boolean required,
1001                 boolean requestGatekeeperPassword, LockscreenCredential chosenPassword,
1002                 LockscreenCredential currentCredential, int userId) {
1003             prepare(utils, required, requestGatekeeperPassword, userId);
1004 
1005             mChosenPassword = chosenPassword;
1006             mCurrentCredential = currentCredential != null ? currentCredential
1007                     : LockscreenCredential.createNone();
1008             mUserId = userId;
1009 
1010             start();
1011         }
1012 
1013         @Override
saveAndVerifyInBackground()1014         protected Pair<Boolean, Intent> saveAndVerifyInBackground() {
1015             final boolean success = mUtils.setLockCredential(
1016                     mChosenPassword, mCurrentCredential, mUserId);
1017             if (success) {
1018                 unifyProfileCredentialIfRequested();
1019             }
1020             Intent result = null;
1021             if (success && mRequestGatekeeperPassword) {
1022                 // If a Gatekeeper Password was requested, invoke the LockSettingsService code
1023                 // path to return a Gatekeeper Password based on the credential that the user
1024                 // chose. This should only be run if the credential was successfully set.
1025                 final VerifyCredentialResponse response = mUtils.verifyCredential(mChosenPassword,
1026                         mUserId, LockPatternUtils.VERIFY_FLAG_REQUEST_GK_PW_HANDLE);
1027 
1028                 if (!response.isMatched() || !response.containsGatekeeperPasswordHandle()) {
1029                     Log.e(TAG, "critical: bad response or missing GK PW handle for known good"
1030                             + " password: " + response.toString());
1031                 }
1032 
1033                 result = new Intent();
1034                 result.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_GK_PW_HANDLE,
1035                         response.getGatekeeperPasswordHandle());
1036             }
1037             return Pair.create(success, result);
1038         }
1039     }
1040 }
1041