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 FooterButton mSkipOrClearButton;
264         private FooterButton mNextButton;
265         private TextView mMessage;
266         protected CheckBox mAutoPinConfirmOption;
267         protected TextView mAutoConfirmSecurityMessage;
268         protected boolean mIsAutoPinConfirmOptionSetManually;
269 
270         private TextChangedHandler mTextChangedHandler;
271 
272         private static final int CONFIRM_EXISTING_REQUEST = 58;
273         static final int RESULT_FINISHED = RESULT_FIRST_USER;
274         /** Used to store the profile type for which pin/password is being set */
275         protected enum ProfileType {
276             None,
277             Managed,
278             Private,
279             Other
280         };
281         protected ProfileType mProfileType;
282 
283         /**
284          * Keep track internally of where the user is in choosing a pattern.
285          */
286         protected enum Stage {
287 
288             Introduction(
289                     R.string.lockpassword_choose_your_password_header, // password
290                     SET_WORK_PROFILE_PASSWORD_HEADER,
291                     R.string.lockpassword_choose_your_profile_password_header,
292                     R.string.lockpassword_choose_your_password_header_for_fingerprint,
293                     R.string.lockpassword_choose_your_password_header_for_face,
294                     R.string.lockpassword_choose_your_password_header_for_biometrics,
295                     R.string.private_space_choose_your_password_header, // private space password
296                     R.string.lockpassword_choose_your_pin_header, // pin
297                     SET_WORK_PROFILE_PIN_HEADER,
298                     R.string.lockpassword_choose_your_profile_pin_header,
299                     R.string.lockpassword_choose_your_pin_header_for_fingerprint,
300                     R.string.lockpassword_choose_your_pin_header_for_face,
301                     R.string.lockpassword_choose_your_pin_header_for_biometrics,
302                     R.string.private_space_choose_your_pin_header, // private space pin
303                     R.string.lock_settings_picker_biometrics_added_security_message,
304                     R.string.lock_settings_picker_biometrics_added_security_message,
305                     R.string.next_label),
306 
307             NeedToConfirm(
308                     R.string.lockpassword_confirm_your_password_header,
309                     REENTER_WORK_PROFILE_PASSWORD_HEADER,
310                     R.string.lockpassword_reenter_your_profile_password_header,
311                     R.string.lockpassword_confirm_your_password_header,
312                     R.string.lockpassword_confirm_your_password_header,
313                     R.string.lockpassword_confirm_your_password_header,
314                     R.string.lockpassword_confirm_your_password_header,
315                     R.string.lockpassword_confirm_your_pin_header,
316                     REENTER_WORK_PROFILE_PIN_HEADER,
317                     R.string.lockpassword_reenter_your_profile_pin_header,
318                     R.string.lockpassword_confirm_your_pin_header,
319                     R.string.lockpassword_confirm_your_pin_header,
320                     R.string.lockpassword_confirm_your_pin_header,
321                     R.string.lockpassword_confirm_your_pin_header,
322                     0,
323                     0,
324                     R.string.lockpassword_confirm_label),
325 
326             ConfirmWrong(
327                     R.string.lockpassword_confirm_passwords_dont_match,
328                     UNDEFINED,
329                     R.string.lockpassword_confirm_passwords_dont_match,
330                     R.string.lockpassword_confirm_passwords_dont_match,
331                     R.string.lockpassword_confirm_passwords_dont_match,
332                     R.string.lockpassword_confirm_passwords_dont_match,
333                     R.string.lockpassword_confirm_passwords_dont_match,
334                     R.string.lockpassword_confirm_pins_dont_match,
335                     UNDEFINED,
336                     R.string.lockpassword_confirm_pins_dont_match,
337                     R.string.lockpassword_confirm_pins_dont_match,
338                     R.string.lockpassword_confirm_pins_dont_match,
339                     R.string.lockpassword_confirm_pins_dont_match,
340                     R.string.lockpassword_confirm_pins_dont_match,
341                     0,
342                     0,
343                     R.string.lockpassword_confirm_label);
344 
Stage(int hintInAlpha, String hintOverrideInAlphaForProfile, int hintInAlphaForProfile, int hintInAlphaForFingerprint, int hintInAlphaForFace, int hintInAlphaForBiometrics, int hintInAlphaForPrivateProfile, int hintInNumeric, String hintOverrideInNumericForProfile, int hintInNumericForProfile, int hintInNumericForFingerprint, int hintInNumericForFace, int hintInNumericForBiometrics, int hintInNumericForPrivateProfile, int messageInAlphaForBiometrics, int messageInNumericForBiometrics, int nextButtonText)345             Stage(int hintInAlpha,
346                     String hintOverrideInAlphaForProfile,
347                     int hintInAlphaForProfile,
348                     int hintInAlphaForFingerprint,
349                     int hintInAlphaForFace,
350                     int hintInAlphaForBiometrics,
351                     int hintInAlphaForPrivateProfile,
352                     int hintInNumeric,
353                     String hintOverrideInNumericForProfile,
354                     int hintInNumericForProfile,
355                     int hintInNumericForFingerprint,
356                     int hintInNumericForFace,
357                     int hintInNumericForBiometrics,
358                     int hintInNumericForPrivateProfile,
359                     int messageInAlphaForBiometrics,
360                     int messageInNumericForBiometrics,
361                     int nextButtonText) {
362 
363                 this.alphaHint = hintInAlpha;
364                 this.alphaHintOverrideForProfile = hintOverrideInAlphaForProfile;
365                 this.alphaHintForManagedProfile = hintInAlphaForProfile;
366                 this.alphaHintForFingerprint = hintInAlphaForFingerprint;
367                 this.alphaHintForFace = hintInAlphaForFace;
368                 this.alphaHintForBiometrics = hintInAlphaForBiometrics;
369                 this.alphaHintForPrivateProfile = hintInAlphaForPrivateProfile;
370 
371                 this.numericHint = hintInNumeric;
372                 this.numericHintOverrideForProfile = hintOverrideInNumericForProfile;
373                 this.numericHintForManagedProfile = hintInNumericForProfile;
374                 this.numericHintForFingerprint = hintInNumericForFingerprint;
375                 this.numericHintForFace = hintInNumericForFace;
376                 this.numericHintForBiometrics = hintInNumericForBiometrics;
377                 this.numericHintForPrivateProfile = hintInNumericForPrivateProfile;
378 
379                 this.alphaMessageForBiometrics = messageInAlphaForBiometrics;
380                 this.numericMessageForBiometrics = messageInNumericForBiometrics;
381 
382                 this.buttonText = nextButtonText;
383             }
384 
385             public static final int TYPE_NONE = 0;
386             public static final int TYPE_FINGERPRINT = 1;
387             public static final int TYPE_FACE = 2;
388             public static final int TYPE_BIOMETRIC = 3;
389 
390             // Password header
391             public final int alphaHint;
392             public final int alphaHintForPrivateProfile;
393             public final String alphaHintOverrideForProfile;
394             public final int alphaHintForManagedProfile;
395             public final int alphaHintForFingerprint;
396             public final int alphaHintForFace;
397             public final int alphaHintForBiometrics;
398 
399             // PIN header
400             public final int numericHint;
401             public final int numericHintForPrivateProfile;
402             public final String numericHintOverrideForProfile;
403             public final int numericHintForManagedProfile;
404             public final int numericHintForFingerprint;
405             public final int numericHintForFace;
406             public final int numericHintForBiometrics;
407 
408             // Password description
409             public final int alphaMessageForBiometrics;
410 
411             // PIN description
412             public final int numericMessageForBiometrics;
413 
414             public final int buttonText;
415 
getHint(Context context, boolean isAlpha, int type, ProfileType profile)416             public String getHint(Context context, boolean isAlpha, int type, ProfileType profile) {
417                 if (isAlpha) {
418                     if (android.os.Flags.allowPrivateProfile()
419                             && android.multiuser.Flags.enablePrivateSpaceFeatures()
420                             && profile.equals(ProfileType.Private)) {
421                         return context.getString(alphaHintForPrivateProfile);
422                     } else if (type == TYPE_FINGERPRINT) {
423                         return context.getString(alphaHintForFingerprint);
424                     } else if (type == TYPE_FACE) {
425                         return context.getString(alphaHintForFace);
426                     } else if (type == TYPE_BIOMETRIC) {
427                         return context.getString(alphaHintForBiometrics);
428                     } else if (profile.equals(ProfileType.Managed)) {
429                         return context.getSystemService(DevicePolicyManager.class).getResources()
430                                 .getString(alphaHintOverrideForProfile,
431                                         () -> context.getString(alphaHintForManagedProfile));
432                     } else {
433                         return context.getString(alphaHint);
434                     }
435                 } else {
436                     if (android.os.Flags.allowPrivateProfile()
437                             && android.multiuser.Flags.enablePrivateSpaceFeatures()
438                             && profile.equals(ProfileType.Private)) {
439                         return context.getString(numericHintForPrivateProfile);
440                     } else if (type == TYPE_FINGERPRINT) {
441                         return context.getString(numericHintForFingerprint);
442                     } else if (type == TYPE_FACE) {
443                         return context.getString(numericHintForFace);
444                     } else if (type == TYPE_BIOMETRIC) {
445                         return context.getString(numericHintForBiometrics);
446                     } else if (profile.equals(ProfileType.Managed)) {
447                         return context.getSystemService(DevicePolicyManager.class).getResources()
448                                 .getString(numericHintOverrideForProfile,
449                                         () -> context.getString(numericHintForManagedProfile));
450                     } else {
451                         return context.getString(numericHint);
452                     }
453                 }
454             }
455 
getMessage(boolean isAlpha, int type)456             public @StringRes int getMessage(boolean isAlpha, int type) {
457                 switch (type) {
458                     case TYPE_FINGERPRINT:
459                     case TYPE_FACE:
460                     case TYPE_BIOMETRIC:
461                         return isAlpha ? alphaMessageForBiometrics : numericMessageForBiometrics;
462 
463                     case TYPE_NONE:
464                     default:
465                         return 0;
466                 }
467             }
468         }
469 
470         // required constructor for fragments
ChooseLockPasswordFragment()471         public ChooseLockPasswordFragment() {
472 
473         }
474 
475         @Override
onCreate(Bundle savedInstanceState)476         public void onCreate(Bundle savedInstanceState) {
477             super.onCreate(savedInstanceState);
478             mLockPatternUtils = new LockPatternUtils(getActivity());
479             Intent intent = getActivity().getIntent();
480             if (!(getActivity() instanceof ChooseLockPassword)) {
481                 throw new SecurityException("Fragment contained in wrong activity");
482             }
483             // Only take this argument into account if it belongs to the current profile.
484             mUserId = Utils.getUserIdFromBundle(getActivity(), intent.getExtras());
485             mProfileType = getProfileType();
486             mForFingerprint = intent.getBooleanExtra(
487                     ChooseLockSettingsHelper.EXTRA_KEY_FOR_FINGERPRINT, false);
488             mForFace = intent.getBooleanExtra(ChooseLockSettingsHelper.EXTRA_KEY_FOR_FACE, false);
489             mForBiometrics = intent.getBooleanExtra(
490                     ChooseLockSettingsHelper.EXTRA_KEY_FOR_BIOMETRICS, false);
491 
492             mPasswordType = intent.getIntExtra(
493                     LockPatternUtils.PASSWORD_TYPE_KEY, PASSWORD_QUALITY_NUMERIC);
494             mUnificationProfileId = intent.getIntExtra(
495                     EXTRA_KEY_UNIFICATION_PROFILE_ID, UserHandle.USER_NULL);
496 
497             mMinComplexity = intent.getIntExtra(EXTRA_KEY_MIN_COMPLEXITY, PASSWORD_COMPLEXITY_NONE);
498             mMinMetrics = intent.getParcelableExtra(EXTRA_KEY_MIN_METRICS);
499             if (mMinMetrics == null) mMinMetrics = new PasswordMetrics(CREDENTIAL_TYPE_NONE);
500 
501             mTextChangedHandler = new TextChangedHandler();
502         }
503 
504         @Override
onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)505         public View onCreateView(LayoutInflater inflater, ViewGroup container,
506                 Bundle savedInstanceState) {
507             return inflater.inflate(R.layout.choose_lock_password, container, false);
508         }
509 
510         @Override
onViewCreated(View view, Bundle savedInstanceState)511         public void onViewCreated(View view, Bundle savedInstanceState) {
512             super.onViewCreated(view, savedInstanceState);
513 
514             mLayout = (GlifLayout) view;
515 
516             // Make the password container consume the optical insets so the edit text is aligned
517             // with the sides of the parent visually.
518             ViewGroup container = view.findViewById(R.id.password_container);
519             container.setOpticalInsets(Insets.NONE);
520 
521             final FooterBarMixin mixin = mLayout.getMixin(FooterBarMixin.class);
522             mixin.setSecondaryButton(
523                     new FooterButton.Builder(getActivity())
524                             .setText(R.string.lockpassword_clear_label)
525                             .setListener(this::onSkipOrClearButtonClick)
526                             .setButtonType(FooterButton.ButtonType.SKIP)
527                             .setTheme(
528                                     com.google.android.setupdesign.R.style.SudGlifButton_Secondary)
529                             .build()
530             );
531             mixin.setPrimaryButton(
532                     new FooterButton.Builder(getActivity())
533                             .setText(R.string.next_label)
534                             .setListener(this::onNextButtonClick)
535                             .setButtonType(FooterButton.ButtonType.NEXT)
536                             .setTheme(com.google.android.setupdesign.R.style.SudGlifButton_Primary)
537                             .build()
538             );
539             mSkipOrClearButton = mixin.getSecondaryButton();
540             mNextButton = mixin.getPrimaryButton();
541 
542             mMessage = view.findViewById(R.id.sud_layout_description);
543             mLayout.setIcon(getActivity().getDrawable(R.drawable.ic_lock));
544 
545             mIsAlphaMode = DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC == mPasswordType
546                     || DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC == mPasswordType
547                     || DevicePolicyManager.PASSWORD_QUALITY_COMPLEX == mPasswordType;
548 
549             final LinearLayout headerLayout = view.findViewById(
550                     com.google.android.setupdesign.R.id.sud_layout_header);
551             setupPasswordRequirementsView(headerLayout);
552 
553             mPasswordRestrictionView.setLayoutManager(new LinearLayoutManager(getActivity()));
554             mPasswordEntry = view.findViewById(R.id.password_entry);
555             mPasswordEntry.setOnEditorActionListener(this);
556             mPasswordEntry.addTextChangedListener(this);
557             mPasswordEntry.requestFocus();
558             mPasswordEntryInputDisabler = new TextViewInputDisabler(mPasswordEntry);
559 
560             // Fetch the AutoPinConfirmOption
561             mAutoPinConfirmOption = view.findViewById(R.id.auto_pin_confirm_enabler);
562             mAutoConfirmSecurityMessage = view.findViewById(R.id.auto_pin_confirm_security_message);
563             mIsAutoPinConfirmOptionSetManually = false;
564             setOnAutoConfirmOptionClickListener();
565             if (mAutoPinConfirmOption != null) {
566                 mAutoPinConfirmOption.setAccessibilityLiveRegion(ACCESSIBILITY_LIVE_REGION_POLITE);
567                 mAutoPinConfirmOption.setVisibility(View.GONE);
568                 mAutoPinConfirmOption.setChecked(false);
569             }
570 
571             final Activity activity = getActivity();
572 
573             int currentType = mPasswordEntry.getInputType();
574             mPasswordEntry.setInputType(mIsAlphaMode ? currentType
575                     : (InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_VARIATION_PASSWORD));
576             if (mIsAlphaMode) {
577                 mPasswordEntry.setContentDescription(
578                         getString(R.string.unlock_set_unlock_password_title));
579             } else {
580                 mPasswordEntry.setContentDescription(
581                         getString(R.string.unlock_set_unlock_pin_title));
582             }
583             // Can't set via XML since setInputType resets the fontFamily to null
584             mPasswordEntry.setTypeface(Typeface.create(
585                     getContext().getString(com.android.internal.R.string.config_headlineFontFamily),
586                     Typeface.NORMAL));
587 
588             Intent intent = getActivity().getIntent();
589             final boolean confirmCredentials = intent.getBooleanExtra(
590                     ChooseLockGeneric.CONFIRM_CREDENTIALS, true);
591             mCurrentCredential = intent.getParcelableExtra(
592                     ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD);
593             mRequestGatekeeperPassword = intent.getBooleanExtra(
594                     ChooseLockSettingsHelper.EXTRA_KEY_REQUEST_GK_PW_HANDLE, false);
595             mRequestWriteRepairModePassword = intent.getBooleanExtra(
596                     ChooseLockSettingsHelper.EXTRA_KEY_REQUEST_WRITE_REPAIR_MODE_PW, false);
597             if (savedInstanceState == null) {
598                 updateStage(Stage.Introduction);
599                 if (confirmCredentials) {
600                     final ChooseLockSettingsHelper.Builder builder =
601                             new ChooseLockSettingsHelper.Builder(getActivity());
602                     builder.setRequestCode(CONFIRM_EXISTING_REQUEST)
603                             .setTitle(getString(R.string.unlock_set_unlock_launch_picker_title))
604                             .setReturnCredentials(true)
605                             .setRequestGatekeeperPasswordHandle(mRequestGatekeeperPassword)
606                             .setRequestWriteRepairModePassword(mRequestWriteRepairModePassword)
607                             .setUserId(mUserId)
608                             .show();
609                 }
610             } else {
611 
612                 // restore from previous state
613                 mFirstPassword = savedInstanceState.getParcelable(KEY_FIRST_PASSWORD);
614                 final String state = savedInstanceState.getString(KEY_UI_STAGE);
615                 if (state != null) {
616                     mUiStage = Stage.valueOf(state);
617                     updateStage(mUiStage);
618                 }
619                 mIsAutoPinConfirmOptionSetManually =
620                         savedInstanceState.getBoolean(KEY_IS_AUTO_CONFIRM_CHECK_MANUALLY_CHANGED);
621 
622                 mCurrentCredential = savedInstanceState.getParcelable(KEY_CURRENT_CREDENTIAL);
623 
624                 // Re-attach to the exiting worker if there is one.
625                 mSaveAndFinishWorker = (SaveAndFinishWorker) getFragmentManager().findFragmentByTag(
626                         FRAGMENT_TAG_SAVE_AND_FINISH);
627             }
628 
629             if (activity instanceof SettingsActivity) {
630                 final SettingsActivity sa = (SettingsActivity) activity;
631                 String title = Stage.Introduction.getHint(
632                         getContext(), mIsAlphaMode, getStageType(), mProfileType);
633                 sa.setTitle(title);
634                 mLayout.setHeaderText(title);
635             }
636         }
637 
638         @Override
onDestroy()639         public void onDestroy() {
640             super.onDestroy();
641             if (mCurrentCredential != null) {
642                 mCurrentCredential.zeroize();
643             }
644             // Force a garbage collection immediately to remove remnant of user password shards
645             // from memory.
646             System.gc();
647             System.runFinalization();
648             System.gc();
649         }
650 
getStageType()651         protected int getStageType() {
652             if (mForFingerprint) {
653                 return Stage.TYPE_FINGERPRINT;
654             } else if (mForFace) {
655                 return Stage.TYPE_FACE;
656             } else if (mForBiometrics) {
657                 return Stage.TYPE_BIOMETRIC;
658             } else {
659                 return Stage.TYPE_NONE;
660             }
661         }
662 
setupPasswordRequirementsView(@ullable ViewGroup view)663         private void setupPasswordRequirementsView(@Nullable ViewGroup view) {
664             if (view == null) {
665                 return;
666             }
667 
668             createHintMessageView(view);
669             mPasswordRestrictionView.setLayoutManager(new LinearLayoutManager(getActivity()));
670             mPasswordRequirementAdapter = new PasswordRequirementAdapter(getActivity());
671             mPasswordRestrictionView.setAdapter(mPasswordRequirementAdapter);
672             view.addView(mPasswordRestrictionView);
673         }
674 
createHintMessageView(ViewGroup view)675         private void createHintMessageView(ViewGroup view) {
676             if (mPasswordRestrictionView != null) {
677                 return;
678             }
679 
680             final TextView sucTitleView = view.findViewById(R.id.suc_layout_title);
681             final ViewGroup.MarginLayoutParams titleLayoutParams =
682                     (ViewGroup.MarginLayoutParams) sucTitleView.getLayoutParams();
683             mPasswordRestrictionView = new RecyclerView(getActivity());
684             final LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(
685                     LinearLayout.LayoutParams.MATCH_PARENT,
686                     LinearLayout.LayoutParams.WRAP_CONTENT);
687             lp.setMargins(titleLayoutParams.leftMargin, getResources().getDimensionPixelSize(
688                     R.dimen.password_requirement_view_margin_top), titleLayoutParams.leftMargin, 0);
689             mPasswordRestrictionView.setLayoutParams(lp);
690         }
691 
692         @Override
getMetricsCategory()693         public int getMetricsCategory() {
694             return SettingsEnums.CHOOSE_LOCK_PASSWORD;
695         }
696 
697         @Override
onResume()698         public void onResume() {
699             super.onResume();
700             updateStage(mUiStage);
701             if (mSaveAndFinishWorker != null) {
702                 mSaveAndFinishWorker.setListener(this);
703             } else {
704                 mPasswordEntry.requestFocus();
705                 mPasswordEntry.scheduleShowSoftInput();
706             }
707         }
708 
709         @Override
onPause()710         public void onPause() {
711             if (mSaveAndFinishWorker != null) {
712                 mSaveAndFinishWorker.setListener(null);
713             }
714             super.onPause();
715         }
716 
717         @Override
onSaveInstanceState(Bundle outState)718         public void onSaveInstanceState(Bundle outState) {
719             super.onSaveInstanceState(outState);
720             outState.putString(KEY_UI_STAGE, mUiStage.name());
721             outState.putParcelable(KEY_FIRST_PASSWORD, mFirstPassword);
722             if (mCurrentCredential != null) {
723                 outState.putParcelable(KEY_CURRENT_CREDENTIAL, mCurrentCredential.duplicate());
724             }
725             outState.putBoolean(KEY_IS_AUTO_CONFIRM_CHECK_MANUALLY_CHANGED,
726                     mIsAutoPinConfirmOptionSetManually);
727         }
728 
729         @Override
onActivityResult(int requestCode, int resultCode, Intent data)730         public void onActivityResult(int requestCode, int resultCode,
731                 Intent data) {
732             super.onActivityResult(requestCode, resultCode, data);
733             switch (requestCode) {
734                 case CONFIRM_EXISTING_REQUEST:
735                     if (resultCode != Activity.RESULT_OK) {
736                         getActivity().setResult(RESULT_FINISHED);
737                         getActivity().finish();
738                     } else {
739                         mCurrentCredential = data.getParcelableExtra(
740                                 ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD);
741                     }
742                     break;
743             }
744         }
745 
getRedactionInterstitialIntent(Context context)746         protected Intent getRedactionInterstitialIntent(Context context) {
747             return RedactionInterstitial.createStartIntent(context, mUserId);
748         }
749 
updateStage(Stage stage)750         protected void updateStage(Stage stage) {
751             final Stage previousStage = mUiStage;
752             mUiStage = stage;
753             updateUi();
754 
755             // If the stage changed, announce the header for accessibility. This
756             // is a no-op when accessibility is disabled.
757             if (previousStage != stage) {
758                 mLayout.announceForAccessibility(mLayout.getHeaderText());
759             }
760         }
761 
762         /**
763          * Validates PIN/Password and returns the validation result and updates mValidationErrors
764          * to reflect validation results.
765          *
766          * @param credential credential the user typed in.
767          * @return whether password satisfies all the requirements.
768          */
769         @VisibleForTesting
validatePassword(LockscreenCredential credential)770         boolean validatePassword(LockscreenCredential credential) {
771             mValidationErrors = PasswordMetrics.validateCredential(mMinMetrics, mMinComplexity,
772                     credential);
773             if (mValidationErrors.isEmpty() && mLockPatternUtils.checkPasswordHistory(
774                         credential.getCredential(), getPasswordHistoryHashFactor(), mUserId)) {
775                 mValidationErrors =
776                         Collections.singletonList(new PasswordValidationError(RECENTLY_USED));
777             }
778             return mValidationErrors.isEmpty();
779         }
780 
781         /**
782          * Lazily compute and return the history hash factor of the current user (mUserId), used for
783          * password history check.
784          */
getPasswordHistoryHashFactor()785         private byte[] getPasswordHistoryHashFactor() {
786             if (mPasswordHistoryHashFactor == null) {
787                 mPasswordHistoryHashFactor = mLockPatternUtils.getPasswordHistoryHashFactor(
788                         mCurrentCredential != null ? mCurrentCredential
789                                 : LockscreenCredential.createNone(), mUserId);
790             }
791             return mPasswordHistoryHashFactor;
792         }
793 
handleNext()794         public void handleNext() {
795             if (mSaveAndFinishWorker != null) return;
796             // TODO(b/120484642): This is a point of entry for passwords from the UI
797             final Editable passwordText = mPasswordEntry.getText();
798             if (TextUtils.isEmpty(passwordText)) {
799                 return;
800             }
801             mChosenPassword = mIsAlphaMode ? LockscreenCredential.createPassword(passwordText)
802                     : LockscreenCredential.createPin(passwordText);
803             if (mUiStage == Stage.Introduction) {
804                 if (validatePassword(mChosenPassword)) {
805                     mFirstPassword = mChosenPassword;
806                     mPasswordEntry.setText("");
807                     updateStage(Stage.NeedToConfirm);
808                 } else {
809                     mChosenPassword.zeroize();
810                 }
811             } else if (mUiStage == Stage.NeedToConfirm) {
812                 if (mChosenPassword.equals(mFirstPassword)) {
813                     startSaveAndFinish();
814                 } else {
815                     CharSequence tmp = mPasswordEntry.getText();
816                     if (tmp != null) {
817                         Selection.setSelection((Spannable) tmp, 0, tmp.length());
818                     }
819                     updateStage(Stage.ConfirmWrong);
820                     mChosenPassword.zeroize();
821                 }
822             }
823         }
824 
setNextEnabled(boolean enabled)825         protected void setNextEnabled(boolean enabled) {
826             mNextButton.setEnabled(enabled);
827         }
828 
setNextText(int text)829         protected void setNextText(int text) {
830             mNextButton.setText(getActivity(), text);
831         }
832 
onSkipOrClearButtonClick(View view)833         protected void onSkipOrClearButtonClick(View view) {
834             mPasswordEntry.setText("");
835         }
836 
onNextButtonClick(View view)837         protected void onNextButtonClick(View view) {
838             handleNext();
839         }
840 
onEditorAction(TextView v, int actionId, KeyEvent event)841         public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
842             // Check if this was the result of hitting the enter or "done" key
843             if (actionId == EditorInfo.IME_NULL
844                     || actionId == EditorInfo.IME_ACTION_DONE
845                     || actionId == EditorInfo.IME_ACTION_NEXT) {
846                 handleNext();
847                 return true;
848             }
849             return false;
850         }
851 
852         /**
853          * @param errorCode error code returned from password validation.
854          * @return an array of messages describing the error, important messages come first.
855          */
convertErrorCodeToMessages()856         String[] convertErrorCodeToMessages() {
857             List<String> messages = new ArrayList<>();
858             for (PasswordValidationError error : mValidationErrors) {
859                 switch (error.errorCode) {
860                     case CONTAINS_INVALID_CHARACTERS:
861                         messages.add(getString(R.string.lockpassword_illegal_character));
862                         break;
863                     case NOT_ENOUGH_UPPER_CASE:
864                         messages.add(StringUtil.getIcuPluralsString(getContext(), error.requirement,
865                                 R.string.lockpassword_password_requires_uppercase));
866                         break;
867                     case NOT_ENOUGH_LOWER_CASE:
868                         messages.add(StringUtil.getIcuPluralsString(getContext(), error.requirement,
869                                 R.string.lockpassword_password_requires_lowercase));
870                         break;
871                     case NOT_ENOUGH_LETTERS:
872                         messages.add(StringUtil.getIcuPluralsString(getContext(), error.requirement,
873                                 R.string.lockpassword_password_requires_letters));
874                         break;
875                     case NOT_ENOUGH_DIGITS:
876                         messages.add(StringUtil.getIcuPluralsString(getContext(), error.requirement,
877                                 R.string.lockpassword_password_requires_numeric));
878                         break;
879                     case NOT_ENOUGH_SYMBOLS:
880                         messages.add(StringUtil.getIcuPluralsString(getContext(), error.requirement,
881                                 R.string.lockpassword_password_requires_symbols));
882                         break;
883                     case NOT_ENOUGH_NON_LETTER:
884                         messages.add(StringUtil.getIcuPluralsString(getContext(), error.requirement,
885                                 R.string.lockpassword_password_requires_nonletter));
886                         break;
887                     case NOT_ENOUGH_NON_DIGITS:
888                         messages.add(StringUtil.getIcuPluralsString(getContext(), error.requirement,
889                                 R.string.lockpassword_password_requires_nonnumerical));
890                         break;
891                     case TOO_SHORT:
892                         String message = StringUtil.getIcuPluralsString(getContext(),
893                                 error.requirement,
894                                 mIsAlphaMode
895                                         ? R.string.lockpassword_password_too_short
896                                         : R.string.lockpassword_pin_too_short);
897                         if (LockPatternUtils.isAutoPinConfirmFeatureAvailable()
898                                 && !mIsAlphaMode
899                                 && error.requirement < MIN_AUTO_PIN_REQUIREMENT_LENGTH) {
900                             Map<String, Object> arguments = new HashMap<>();
901                             arguments.put("count", error.requirement);
902                             arguments.put("minAutoConfirmLen", MIN_AUTO_PIN_REQUIREMENT_LENGTH);
903                             message = StringUtil.getIcuPluralsString(getContext(),
904                                     arguments,
905                                     R.string.lockpassword_pin_too_short_autoConfirm_extra_message);
906                         }
907                         messages.add(message);
908                         break;
909                     case TOO_SHORT_WHEN_ALL_NUMERIC:
910                         messages.add(
911                                 StringUtil.getIcuPluralsString(getContext(), error.requirement,
912                                         R.string.lockpassword_password_too_short_all_numeric));
913                         break;
914                     case TOO_LONG:
915                         messages.add(StringUtil.getIcuPluralsString(getContext(),
916                                 error.requirement + 1, mIsAlphaMode
917                                         ? R.string.lockpassword_password_too_long
918                                         : R.string.lockpassword_pin_too_long));
919                         break;
920                     case CONTAINS_SEQUENCE:
921                         messages.add(getString(R.string.lockpassword_pin_no_sequential_digits));
922                         break;
923                     case RECENTLY_USED:
924                         DevicePolicyManager devicePolicyManager =
925                                 getContext().getSystemService(DevicePolicyManager.class);
926                         if (mIsAlphaMode) {
927                             messages.add(devicePolicyManager.getResources().getString(
928                                     PASSWORD_RECENTLY_USED,
929                                     () -> getString(R.string.lockpassword_password_recently_used)));
930                         } else {
931                             messages.add(devicePolicyManager.getResources().getString(
932                                     PIN_RECENTLY_USED,
933                                     () -> getString(R.string.lockpassword_pin_recently_used)));
934                         }
935                         break;
936                     default:
937                         Log.wtf(TAG, "unknown error validating password: " + error);
938                 }
939             }
940 
941             return messages.toArray(new String[0]);
942         }
943 
944         /**
945          * Update the hint based on current Stage and length of password entry
946          */
updateUi()947         protected void updateUi() {
948             final boolean canInput = mSaveAndFinishWorker == null;
949 
950             LockscreenCredential password = mIsAlphaMode
951                     ? LockscreenCredential.createPassword(mPasswordEntry.getText())
952                     : LockscreenCredential.createPin(mPasswordEntry.getText());
953             final int length = password.size();
954             if (mUiStage == Stage.Introduction) {
955                 mPasswordRestrictionView.setVisibility(View.VISIBLE);
956                 final boolean passwordCompliant = validatePassword(password);
957                 String[] messages = convertErrorCodeToMessages();
958                 // Update the fulfillment of requirements.
959                 mPasswordRequirementAdapter.setRequirements(messages);
960                 // set the visibility of pin_auto_confirm option accordingly
961                 setAutoPinConfirmOption(passwordCompliant, length);
962                 // Enable/Disable the next button accordingly.
963                 setNextEnabled(passwordCompliant);
964             } else {
965                 // Hide password requirement view when we are just asking user to confirm the pw.
966                 mPasswordRestrictionView.setVisibility(View.GONE);
967                 setHeaderText(mUiStage.getHint(getContext(), mIsAlphaMode, getStageType(),
968                         mProfileType));
969                 setNextEnabled(canInput && length >= LockPatternUtils.MIN_LOCK_PASSWORD_SIZE);
970                 mSkipOrClearButton.setVisibility(toVisibility(canInput && length > 0));
971 
972                 // Hide the pin_confirm option when we are just asking user to confirm the pwd.
973                 mAutoPinConfirmOption.setVisibility(View.GONE);
974                 mAutoConfirmSecurityMessage.setVisibility(View.GONE);
975             }
976             final int stage = getStageType();
977             if (getStageType() != Stage.TYPE_NONE) {
978                 int message = mUiStage.getMessage(mIsAlphaMode, stage);
979                 if (message != 0) {
980                     mMessage.setVisibility(View.VISIBLE);
981                     mMessage.setText(message);
982                 } else {
983                     mMessage.setVisibility(View.INVISIBLE);
984                 }
985             } else {
986                 mMessage.setVisibility(View.GONE);
987             }
988 
989             setNextText(mUiStage.buttonText);
990             mPasswordEntryInputDisabler.setInputEnabled(canInput);
991             password.zeroize();
992         }
993 
toVisibility(boolean visibleOrGone)994         protected int toVisibility(boolean visibleOrGone) {
995             return visibleOrGone ? View.VISIBLE : View.GONE;
996         }
997 
setAutoPinConfirmOption(boolean enabled, int length)998         private void setAutoPinConfirmOption(boolean enabled, int length) {
999             if (!LockPatternUtils.isAutoPinConfirmFeatureAvailable()
1000                     || mAutoPinConfirmOption == null) {
1001                 return;
1002             }
1003             if (enabled && !mIsAlphaMode && isAutoPinConfirmPossible(length)) {
1004                 mAutoPinConfirmOption.setVisibility(View.VISIBLE);
1005                 mAutoConfirmSecurityMessage.setVisibility(View.VISIBLE);
1006                 if (!mIsAutoPinConfirmOptionSetManually) {
1007                     mAutoPinConfirmOption.setChecked(length == MIN_AUTO_PIN_REQUIREMENT_LENGTH);
1008                 }
1009             } else {
1010                 mAutoPinConfirmOption.setVisibility(View.GONE);
1011                 mAutoConfirmSecurityMessage.setVisibility(View.GONE);
1012                 mAutoPinConfirmOption.setChecked(false);
1013             }
1014         }
1015 
isAutoPinConfirmPossible(int currentPinLength)1016         private boolean isAutoPinConfirmPossible(int currentPinLength) {
1017             return currentPinLength >= MIN_AUTO_PIN_REQUIREMENT_LENGTH;
1018         }
1019 
setOnAutoConfirmOptionClickListener()1020         private void setOnAutoConfirmOptionClickListener() {
1021             if (mAutoPinConfirmOption != null) {
1022                 mAutoPinConfirmOption.setOnClickListener((v) -> {
1023                     mIsAutoPinConfirmOptionSetManually = true;
1024                 });
1025             }
1026         }
1027 
setHeaderText(String text)1028         private void setHeaderText(String text) {
1029             // Only set the text if it is different than the existing one to avoid announcing again.
1030             if (!TextUtils.isEmpty(mLayout.getHeaderText())
1031                     && mLayout.getHeaderText().toString().equals(text)) {
1032                 return;
1033             }
1034             mLayout.setHeaderText(text);
1035         }
1036 
afterTextChanged(Editable s)1037         public void afterTextChanged(Editable s) {
1038             // Changing the text while error displayed resets to NeedToConfirm state
1039             if (mUiStage == Stage.ConfirmWrong) {
1040                 mUiStage = Stage.NeedToConfirm;
1041             }
1042             // Schedule the UI update.
1043             mTextChangedHandler.notifyAfterTextChanged();
1044         }
1045 
beforeTextChanged(CharSequence s, int start, int count, int after)1046         public void beforeTextChanged(CharSequence s, int start, int count, int after) {
1047 
1048         }
1049 
onTextChanged(CharSequence s, int start, int before, int count)1050         public void onTextChanged(CharSequence s, int start, int before, int count) {
1051 
1052         }
1053 
startSaveAndFinish()1054         private void startSaveAndFinish() {
1055             if (mSaveAndFinishWorker != null) {
1056                 Log.w(TAG, "startSaveAndFinish with an existing SaveAndFinishWorker.");
1057                 return;
1058             }
1059 
1060             ConfirmDeviceCredentialUtils.hideImeImmediately(
1061                     getActivity().getWindow().getDecorView());
1062 
1063             mPasswordEntryInputDisabler.setInputEnabled(false);
1064             mSaveAndFinishWorker = new SaveAndFinishWorker();
1065             mSaveAndFinishWorker
1066                     .setListener(this)
1067                     .setRequestGatekeeperPasswordHandle(mRequestGatekeeperPassword)
1068                     .setRequestWriteRepairModePassword(mRequestWriteRepairModePassword);
1069 
1070             getFragmentManager().beginTransaction().add(mSaveAndFinishWorker,
1071                     FRAGMENT_TAG_SAVE_AND_FINISH).commit();
1072             getFragmentManager().executePendingTransactions();
1073 
1074             final Intent intent = getActivity().getIntent();
1075             if (mUnificationProfileId != UserHandle.USER_NULL) {
1076                 try (LockscreenCredential profileCredential = (LockscreenCredential)
1077                         intent.getParcelableExtra(EXTRA_KEY_UNIFICATION_PROFILE_CREDENTIAL)) {
1078                     mSaveAndFinishWorker.setProfileToUnify(mUnificationProfileId,
1079                             profileCredential);
1080                 }
1081             }
1082             // update the setting before triggering the password save workflow,
1083             // so that pinLength information is stored accordingly when setting is turned on.
1084             mLockPatternUtils.setAutoPinConfirm(
1085                     (mAutoPinConfirmOption != null && mAutoPinConfirmOption.isChecked()),
1086                     mUserId);
1087 
1088             mSaveAndFinishWorker.start(mLockPatternUtils,
1089                     mChosenPassword, mCurrentCredential, mUserId);
1090         }
1091 
1092         @Override
onChosenLockSaveFinished(boolean wasSecureBefore, Intent resultData)1093         public void onChosenLockSaveFinished(boolean wasSecureBefore, Intent resultData) {
1094             getActivity().setResult(RESULT_FINISHED, resultData);
1095 
1096             if (mChosenPassword != null) {
1097                 mChosenPassword.zeroize();
1098             }
1099             if (mCurrentCredential != null) {
1100                 mCurrentCredential.zeroize();
1101             }
1102             if (mFirstPassword != null) {
1103                 mFirstPassword.zeroize();
1104             }
1105 
1106             mPasswordEntry.setText("");
1107 
1108             if (!wasSecureBefore) {
1109                 Intent intent = getRedactionInterstitialIntent(getActivity());
1110                 if (intent != null) {
1111                     startActivity(intent);
1112                 }
1113             }
1114 
1115             if (mLayout != null) {
1116                 mLayout.announceForAccessibility(
1117                         getString(R.string.accessibility_setup_password_complete));
1118             }
1119 
1120             getActivity().finish();
1121         }
1122 
1123         class TextChangedHandler extends Handler {
1124             private static final int ON_TEXT_CHANGED = 1;
1125             private static final int DELAY_IN_MILLISECOND = 100;
1126 
1127             /**
1128              * With the introduction of delay, we batch processing the text changed event to reduce
1129              * unnecessary UI updates.
1130              */
notifyAfterTextChanged()1131             private void notifyAfterTextChanged() {
1132                 removeMessages(ON_TEXT_CHANGED);
1133                 sendEmptyMessageDelayed(ON_TEXT_CHANGED, DELAY_IN_MILLISECOND);
1134             }
1135 
1136             @Override
handleMessage(Message msg)1137             public void handleMessage(Message msg) {
1138                 if (getActivity() == null) {
1139                     return;
1140                 }
1141                 if (msg.what == ON_TEXT_CHANGED) {
1142                     updateUi();
1143                 }
1144             }
1145         }
1146 
getProfileType()1147         private ProfileType getProfileType() {
1148             UserManager userManager = getContext().createContextAsUser(UserHandle.of(mUserId),
1149                     /*flags=*/0).getSystemService(UserManager.class);
1150             if (userManager.isManagedProfile()) {
1151                 return ProfileType.Managed;
1152             } else if (android.os.Flags.allowPrivateProfile()
1153                     && android.multiuser.Flags.enablePrivateSpaceFeatures()
1154                     && userManager.isPrivateProfile()) {
1155                 return ProfileType.Private;
1156             } else if (userManager.isProfile()) {
1157                 return ProfileType.Other;
1158             }
1159             return ProfileType.None;
1160         }
1161     }
1162 }
1163