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