Introduce LockscreenCredential

Bug: 65239740
Test: make RunSettingsRoboTests ROBOTEST_FILTER=com.android.settings.password
Change-Id: Icb73d639291d6d2eda8015e18e93d0906f916bb2
diff --git a/src/com/android/settings/CryptKeeper.java b/src/com/android/settings/CryptKeeper.java
index 05054b4..604333f 100644
--- a/src/com/android/settings/CryptKeeper.java
+++ b/src/com/android/settings/CryptKeeper.java
@@ -727,7 +727,7 @@
         public void onPatternDetected(List<LockPatternView.Cell> pattern) {
             mLockPatternView.setEnabled(false);
             if (pattern.size() >= MIN_LENGTH_BEFORE_REPORT) {
-                new DecryptTask().execute(LockPatternUtils.patternToString(pattern));
+                new DecryptTask().execute(new String(LockPatternUtils.patternToByteArray(pattern)));
             } else {
                 // Allow user to make as many of these as they want.
                 fakeUnlockAttempt(mLockPatternView);
diff --git a/src/com/android/settings/password/ChooseLockGeneric.java b/src/com/android/settings/password/ChooseLockGeneric.java
index f86292b..4f1b050 100644
--- a/src/com/android/settings/password/ChooseLockGeneric.java
+++ b/src/com/android/settings/password/ChooseLockGeneric.java
@@ -61,6 +61,7 @@
 import androidx.preference.PreferenceScreen;
 
 import com.android.internal.widget.LockPatternUtils;
+import com.android.internal.widget.LockscreenCredential;
 import com.android.settings.EncryptionInterstitial;
 import com.android.settings.EventLogTags;
 import com.android.settings.R;
@@ -151,7 +152,7 @@
         private boolean mPasswordConfirmed = false;
         private boolean mWaitingForConfirmation = false;
         private boolean mForChangeCredRequiredForBoot = false;
-        private byte[] mUserPassword;
+        private LockscreenCredential mUserPassword;
         private LockPatternUtils mLockPatternUtils;
         private FingerprintManager mFingerprintManager;
         private FaceManager mFaceManager;
@@ -207,7 +208,7 @@
                 .getBooleanExtra(CONFIRM_CREDENTIALS, true);
             if (getActivity() instanceof ChooseLockGeneric.InternalActivity) {
                 mPasswordConfirmed = !confirmCredentials;
-                mUserPassword = getActivity().getIntent().getByteArrayExtra(
+                mUserPassword = getActivity().getIntent().getParcelableExtra(
                         ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD);
             }
 
@@ -233,7 +234,7 @@
                 mPasswordConfirmed = savedInstanceState.getBoolean(PASSWORD_CONFIRMED);
                 mWaitingForConfirmation = savedInstanceState.getBoolean(WAITING_FOR_CONFIRMATION);
                 if (mUserPassword == null) {
-                    mUserPassword = savedInstanceState.getByteArray(
+                    mUserPassword = savedInstanceState.getParcelable(
                             ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD);
                 }
             }
@@ -392,11 +393,11 @@
             if (requestCode == CONFIRM_EXISTING_REQUEST && resultCode == Activity.RESULT_OK) {
                 mPasswordConfirmed = true;
                 mUserPassword = data != null
-                    ? data.getByteArrayExtra(ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD)
+                    ? data.getParcelableExtra(ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD)
                     : null;
                 updatePreferencesOrFinish(false /* isRecreatingActivity */);
                 if (mForChangeCredRequiredForBoot) {
-                    if (!(mUserPassword == null || mUserPassword.length == 0)) {
+                    if (mUserPassword != null && !mUserPassword.isNone()) {
                         maybeEnableEncryption(
                                 mLockPatternUtils.getKeyguardStoredPasswordQuality(mUserId), false);
                     } else {
@@ -459,7 +460,7 @@
             outState.putBoolean(PASSWORD_CONFIRMED, mPasswordConfirmed);
             outState.putBoolean(WAITING_FOR_CONFIRMATION, mWaitingForConfirmation);
             if (mUserPassword != null) {
-                outState.putByteArray(ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD, mUserPassword);
+                outState.putParcelable(ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD, mUserPassword);
             }
         }
 
@@ -682,7 +683,7 @@
             setPreferenceSummary(ScreenLockType.MANAGED, R.string.secure_lock_encryption_warning);
         }
 
-        protected Intent getLockManagedPasswordIntent(byte[] password) {
+        protected Intent getLockManagedPasswordIntent(LockscreenCredential password) {
             return mManagedPasswordProvider.createIntent(false, password);
         }
 
@@ -759,7 +760,8 @@
             if (quality == DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED) {
                 // Clearing of user biometrics when screen lock is cleared is done at
                 // LockSettingsService.removeBiometricsForUser().
-                mChooseLockSettingsHelper.utils().clearLock(mUserPassword, mUserId);
+                mChooseLockSettingsHelper.utils().setLockCredential(
+                        LockscreenCredential.createNone(), mUserPassword, mUserId);
                 mChooseLockSettingsHelper.utils().setLockScreenDisabled(disabled, mUserId);
                 getActivity().setResult(Activity.RESULT_OK);
                 finish();
diff --git a/src/com/android/settings/password/ChooseLockPassword.java b/src/com/android/settings/password/ChooseLockPassword.java
index 820aecb..6872543 100644
--- a/src/com/android/settings/password/ChooseLockPassword.java
+++ b/src/com/android/settings/password/ChooseLockPassword.java
@@ -63,6 +63,7 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.widget.LockPatternUtils;
 import com.android.internal.widget.LockPatternUtils.RequestThrottledException;
+import com.android.internal.widget.LockscreenCredential;
 import com.android.internal.widget.TextViewInputDisabler;
 import com.android.settings.EncryptionInterstitial;
 import com.android.settings.R;
@@ -124,7 +125,7 @@
             return this;
         }
 
-        public IntentBuilder setPassword(byte[] password) {
+        public IntentBuilder setPassword(LockscreenCredential password) {
             mIntent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD, password);
             return this;
         }
@@ -180,13 +181,13 @@
 
     public static class ChooseLockPasswordFragment extends InstrumentedFragment
             implements OnEditorActionListener, TextWatcher, SaveAndFinishWorker.Listener {
-        private static final String KEY_FIRST_PIN = "first_pin";
+        private static final String KEY_FIRST_PASSWORD = "first_password";
         private static final String KEY_UI_STAGE = "ui_stage";
-        private static final String KEY_CURRENT_PASSWORD = "current_password";
+        private static final String KEY_CURRENT_CREDENTIAL = "current_credential";
         private static final String FRAGMENT_TAG_SAVE_AND_FINISH = "save_and_finish_worker";
 
-        private byte[] mCurrentPassword;
-        private byte[] mChosenPassword;
+        private LockscreenCredential mCurrentCredential;
+        private LockscreenCredential mChosenPassword;
         private boolean mHasChallenge;
         private long mChallenge;
         private ImeAwareEditText mPasswordEntry;
@@ -215,7 +216,7 @@
         protected boolean mForFingerprint;
         protected boolean mForFace;
 
-        private byte[] mFirstPin;
+        private LockscreenCredential mFirstPassword;
         private RecyclerView mPasswordRestrictionView;
         protected boolean mIsAlphaMode;
         protected FooterButton mSkipOrClearButton;
@@ -394,13 +395,13 @@
                 SaveAndFinishWorker w = new SaveAndFinishWorker();
                 final boolean required = getActivity().getIntent().getBooleanExtra(
                         EncryptionInterstitial.EXTRA_REQUIRE_PASSWORD, true);
-                byte[] currentBytes = intent.getByteArrayExtra(
+                LockscreenCredential currentCredential = intent.getParcelableExtra(
                         ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD);
 
                 w.setBlocking(true);
                 w.setListener(this);
                 w.start(mChooseLockSettingsHelper.utils(), required, false, 0,
-                        currentBytes, currentBytes, mRequestedQuality, mUserId);
+                        currentCredential, currentCredential, mUserId);
             }
             mTextChangedHandler = new TextChangedHandler();
         }
@@ -482,7 +483,7 @@
             Intent intent = getActivity().getIntent();
             final boolean confirmCredentials = intent.getBooleanExtra(
                     ChooseLockGeneric.CONFIRM_CREDENTIALS, true);
-            mCurrentPassword = intent.getByteArrayExtra(
+            mCurrentCredential = intent.getParcelableExtra(
                     ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD);
             mHasChallenge = intent.getBooleanExtra(
                     ChooseLockSettingsHelper.EXTRA_KEY_HAS_CHALLENGE, false);
@@ -497,15 +498,15 @@
             } else {
 
                 // restore from previous state
-                mFirstPin = savedInstanceState.getByteArray(KEY_FIRST_PIN);
+                mFirstPassword = savedInstanceState.getParcelable(KEY_FIRST_PASSWORD);
                 final String state = savedInstanceState.getString(KEY_UI_STAGE);
                 if (state != null) {
                     mUiStage = Stage.valueOf(state);
                     updateStage(mUiStage);
                 }
 
-                if (mCurrentPassword == null) {
-                    mCurrentPassword = savedInstanceState.getByteArray(KEY_CURRENT_PASSWORD);
+                if (mCurrentCredential == null) {
+                    mCurrentCredential = savedInstanceState.getParcelable(KEY_CURRENT_CREDENTIAL);
                 }
 
                 // Re-attach to the exiting worker if there is one.
@@ -563,8 +564,8 @@
         public void onSaveInstanceState(Bundle outState) {
             super.onSaveInstanceState(outState);
             outState.putString(KEY_UI_STAGE, mUiStage.name());
-            outState.putByteArray(KEY_FIRST_PIN, mFirstPin);
-            outState.putByteArray(KEY_CURRENT_PASSWORD, mCurrentPassword);
+            outState.putParcelable(KEY_FIRST_PASSWORD, mFirstPassword);
+            outState.putParcelable(KEY_CURRENT_CREDENTIAL, mCurrentCredential);
         }
 
         @Override
@@ -577,7 +578,7 @@
                         getActivity().setResult(RESULT_FINISHED);
                         getActivity().finish();
                     } else {
-                        mCurrentPassword = data.getByteArrayExtra(
+                        mCurrentCredential = data.getParcelableExtra(
                                 ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD);
                     }
                     break;
@@ -722,7 +723,8 @@
          * @return the validation result.
          */
         @VisibleForTesting
-        int validatePassword(byte[] password) {
+        int validatePassword(LockscreenCredential credential) {
+            final byte[] password = credential.getCredential();
             int errorCode = NO_ERROR;
             final PasswordMetrics metrics = PasswordMetrics.computeForPassword(password);
             mergeMinComplexityAndDpmRequirements(metrics.quality);
@@ -812,7 +814,8 @@
         private byte[] getPasswordHistoryHashFactor() {
             if (mPasswordHistoryHashFactor == null) {
                 mPasswordHistoryHashFactor = mLockPatternUtils.getPasswordHistoryHashFactor(
-                        mCurrentPassword, mUserId);
+                        mCurrentCredential != null ? mCurrentCredential
+                                : LockscreenCredential.createNone(), mUserId);
             }
             return mPasswordHistoryHashFactor;
         }
@@ -820,20 +823,22 @@
         public void handleNext() {
             if (mSaveAndFinishWorker != null) return;
             // TODO(b/120484642): This is a point of entry for passwords from the UI
-            mChosenPassword = LockPatternUtils.charSequenceToByteArray(mPasswordEntry.getText());
-            if (mChosenPassword == null || mChosenPassword.length == 0) {
+            final Editable passwordText = mPasswordEntry.getText();
+            if (TextUtils.isEmpty(passwordText)) {
                 return;
             }
+            mChosenPassword = mIsAlphaMode ? LockscreenCredential.createPassword(passwordText)
+                    : LockscreenCredential.createPin(passwordText);
             if (mUiStage == Stage.Introduction) {
                 if (validatePassword(mChosenPassword) == NO_ERROR) {
-                    mFirstPin = mChosenPassword;
+                    mFirstPassword = mChosenPassword;
                     mPasswordEntry.setText("");
                     updateStage(Stage.NeedToConfirm);
                 } else {
-                    Arrays.fill(mChosenPassword, (byte) 0);
+                    mChosenPassword.zeroize();
                 }
             } else if (mUiStage == Stage.NeedToConfirm) {
-                if (Arrays.equals(mFirstPin, mChosenPassword)) {
+                if (mChosenPassword.equals(mFirstPassword)) {
                     startSaveAndFinish();
                 } else {
                     CharSequence tmp = mPasswordEntry.getText();
@@ -841,7 +846,7 @@
                         Selection.setSelection((Spannable) tmp, 0, tmp.length());
                     }
                     updateStage(Stage.ConfirmWrong);
-                    Arrays.fill(mChosenPassword, (byte) 0);
+                    mChosenPassword.zeroize();
                 }
             }
         }
@@ -954,8 +959,11 @@
          */
         protected void updateUi() {
             final boolean canInput = mSaveAndFinishWorker == null;
-            byte[] password = LockPatternUtils.charSequenceToByteArray(mPasswordEntry.getText());
-            final int length = password.length;
+
+            LockscreenCredential password = mIsAlphaMode
+                    ? LockscreenCredential.createPasswordOrNone(mPasswordEntry.getText())
+                    : LockscreenCredential.createPinOrNone(mPasswordEntry.getText());
+            final int length = password.size();
             if (mUiStage == Stage.Introduction) {
                 mPasswordRestrictionView.setVisibility(View.VISIBLE);
                 final int errorCode = validatePassword(password);
@@ -981,7 +989,7 @@
 
             setNextText(mUiStage.buttonText);
             mPasswordEntryInputDisabler.setInputEnabled(canInput);
-            Arrays.fill(password, (byte) 0);
+            password.zeroize();
         }
 
         protected int toVisibility(boolean visibleOrGone) {
@@ -1033,7 +1041,7 @@
             final boolean required = getActivity().getIntent().getBooleanExtra(
                     EncryptionInterstitial.EXTRA_REQUIRE_PASSWORD, true);
             mSaveAndFinishWorker.start(mLockPatternUtils, required, mHasChallenge, mChallenge,
-                    mChosenPassword, mCurrentPassword, mRequestedQuality, mUserId);
+                    mChosenPassword, mCurrentCredential, mUserId);
         }
 
         @Override
@@ -1041,13 +1049,13 @@
             getActivity().setResult(RESULT_FINISHED, resultData);
 
             if (mChosenPassword != null) {
-                Arrays.fill(mChosenPassword, (byte) 0);
+                mChosenPassword.zeroize();
             }
-            if (mCurrentPassword != null) {
-                Arrays.fill(mCurrentPassword, (byte) 0);
+            if (mCurrentCredential != null) {
+                mCurrentCredential.zeroize();
             }
-            if (mFirstPin != null) {
-                Arrays.fill(mFirstPin, (byte) 0);
+            if (mFirstPassword != null) {
+                mFirstPassword.zeroize();
             }
 
             mPasswordEntry.setText("");
@@ -1088,18 +1096,18 @@
 
     public static class SaveAndFinishWorker extends SaveChosenLockWorkerBase {
 
-        private byte[] mChosenPassword;
-        private byte[] mCurrentPassword;
-        private int mRequestedQuality;
+        private LockscreenCredential mChosenPassword;
+        private LockscreenCredential mCurrentCredential;
 
         public void start(LockPatternUtils utils, boolean required,
                 boolean hasChallenge, long challenge,
-                byte[] chosenPassword, byte[] currentPassword, int requestedQuality, int userId) {
+                LockscreenCredential chosenPassword, LockscreenCredential currentCredential,
+                int userId) {
             prepare(utils, required, hasChallenge, challenge, userId);
 
             mChosenPassword = chosenPassword;
-            mCurrentPassword = currentPassword;
-            mRequestedQuality = requestedQuality;
+            mCurrentCredential = currentCredential != null ? currentCredential
+                    : LockscreenCredential.createNone();
             mUserId = userId;
 
             start();
@@ -1107,13 +1115,13 @@
 
         @Override
         protected Pair<Boolean, Intent> saveAndVerifyInBackground() {
-            final boolean success = mUtils.saveLockPassword(
-                    mChosenPassword, mCurrentPassword, mRequestedQuality, mUserId);
+            final boolean success = mUtils.setLockCredential(
+                    mChosenPassword, mCurrentCredential, mUserId);
             Intent result = null;
             if (success && mHasChallenge) {
                 byte[] token;
                 try {
-                    token = mUtils.verifyPassword(mChosenPassword, mChallenge, mUserId);
+                    token = mUtils.verifyCredential(mChosenPassword, mChallenge, mUserId);
                 } catch (RequestThrottledException e) {
                     token = null;
                 }
diff --git a/src/com/android/settings/password/ChooseLockPattern.java b/src/com/android/settings/password/ChooseLockPattern.java
index 5de4e3a..b81b0c4 100644
--- a/src/com/android/settings/password/ChooseLockPattern.java
+++ b/src/com/android/settings/password/ChooseLockPattern.java
@@ -36,12 +36,14 @@
 
 import androidx.fragment.app.Fragment;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.widget.LinearLayoutWithDefaultTouchRecepient;
 import com.android.internal.widget.LockPatternUtils;
 import com.android.internal.widget.LockPatternUtils.RequestThrottledException;
 import com.android.internal.widget.LockPatternView;
 import com.android.internal.widget.LockPatternView.Cell;
 import com.android.internal.widget.LockPatternView.DisplayMode;
+import com.android.internal.widget.LockscreenCredential;
 import com.android.settings.EncryptionInterstitial;
 import com.android.settings.R;
 import com.android.settings.SettingsActivity;
@@ -116,7 +118,7 @@
             return this;
         }
 
-        public IntentBuilder setPattern(byte[] pattern) {
+        public IntentBuilder setPattern(LockscreenCredential pattern) {
             mIntent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD, pattern);
             return this;
         }
@@ -188,7 +190,7 @@
 
         private static final String FRAGMENT_TAG_SAVE_AND_FINISH = "save_and_finish_worker";
 
-        private byte[] mCurrentPattern;
+        private LockscreenCredential mCurrentCredential;
         private boolean mHasChallenge;
         private long mChallenge;
         protected TextView mTitleText;
@@ -198,7 +200,7 @@
         protected TextView mFooterText;
         protected FooterButton mSkipOrClearButton;
         private FooterButton mNextButton;
-        protected List<LockPatternView.Cell> mChosenPattern = null;
+        @VisibleForTesting protected LockscreenCredential mChosenPattern;
         private ColorStateList mDefaultHeaderColorList;
 
         // ScrollView that contains title and header, only exist in land mode
@@ -225,7 +227,7 @@
                         getActivity().setResult(RESULT_FINISHED);
                         getActivity().finish();
                     } else {
-                        mCurrentPattern = data.getByteArrayExtra(
+                        mCurrentCredential = data.getParcelableExtra(
                                 ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD);
                     }
 
@@ -262,16 +264,19 @@
                     if (mUiStage == Stage.NeedToConfirm || mUiStage == Stage.ConfirmWrong) {
                         if (mChosenPattern == null) throw new IllegalStateException(
                                 "null chosen pattern in stage 'need to confirm");
-                        if (mChosenPattern.equals(pattern)) {
-                            updateStage(Stage.ChoiceConfirmed);
-                        } else {
-                            updateStage(Stage.ConfirmWrong);
+                        try (LockscreenCredential confirmPattern =
+                                LockscreenCredential.createPattern(pattern)) {
+                            if (mChosenPattern.equals(confirmPattern)) {
+                                updateStage(Stage.ChoiceConfirmed);
+                            } else {
+                                updateStage(Stage.ConfirmWrong);
+                            }
                         }
                     } else if (mUiStage == Stage.Introduction || mUiStage == Stage.ChoiceTooShort){
                         if (pattern.size() < LockPatternUtils.MIN_LOCK_PATTERN_SIZE) {
                             updateStage(Stage.ChoiceTooShort);
                         } else {
-                            mChosenPattern = new ArrayList<LockPatternView.Cell>(pattern);
+                            mChosenPattern = LockscreenCredential.createPattern(pattern);
                             updateStage(Stage.FirstChoiceValid);
                         }
                     } else {
@@ -458,12 +463,12 @@
                 SaveAndFinishWorker w = new SaveAndFinishWorker();
                 final boolean required = getActivity().getIntent().getBooleanExtra(
                         EncryptionInterstitial.EXTRA_REQUIRE_PASSWORD, true);
-                byte[] current = intent.getByteArrayExtra(
+                LockscreenCredential current = intent.getParcelableExtra(
                         ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD);
                 w.setBlocking(true);
                 w.setListener(this);
                 w.start(mChooseLockSettingsHelper.utils(), required,
-                        false, 0, LockPatternUtils.byteArrayToPattern(current), current, mUserId);
+                        false, 0, current, current, mUserId);
             }
             mForFingerprint = intent.getBooleanExtra(
                     ChooseLockSettingsHelper.EXTRA_KEY_FOR_FINGERPRINT, false);
@@ -541,8 +546,8 @@
             final boolean confirmCredentials = getActivity().getIntent()
                     .getBooleanExtra(ChooseLockGeneric.CONFIRM_CREDENTIALS, true);
             Intent intent = getActivity().getIntent();
-            mCurrentPattern =
-                    intent.getByteArrayExtra(ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD);
+            mCurrentCredential =
+                    intent.getParcelableExtra(ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD);
             mHasChallenge = intent.getBooleanExtra(
                     ChooseLockSettingsHelper.EXTRA_KEY_HAS_CHALLENGE, false);
             mChallenge = intent.getLongExtra(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE, 0);
@@ -565,13 +570,10 @@
                 }
             } else {
                 // restore from previous state
-                final byte[] pattern = savedInstanceState.getByteArray(KEY_PATTERN_CHOICE);
-                if (pattern != null) {
-                    mChosenPattern = LockPatternUtils.byteArrayToPattern(pattern);
-                }
+                mChosenPattern = savedInstanceState.getParcelable(KEY_PATTERN_CHOICE);
 
-                if (mCurrentPattern == null) {
-                    mCurrentPattern = savedInstanceState.getByteArray(KEY_CURRENT_PATTERN);
+                if (mCurrentCredential == null) {
+                    mCurrentCredential = savedInstanceState.getParcelable(KEY_CURRENT_PATTERN);
                 }
                 updateStage(Stage.values()[savedInstanceState.getInt(KEY_UI_STAGE)]);
 
@@ -606,6 +608,7 @@
 
         public void handleLeftButton() {
             if (mUiStage.leftMode == LeftButtonMode.Retry) {
+                mChosenPattern.zeroize();
                 mChosenPattern = null;
                 mLockPatternView.clearPattern();
                 updateStage(Stage.Introduction);
@@ -667,12 +670,11 @@
 
             outState.putInt(KEY_UI_STAGE, mUiStage.ordinal());
             if (mChosenPattern != null) {
-                outState.putByteArray(KEY_PATTERN_CHOICE,
-                        LockPatternUtils.patternToByteArray(mChosenPattern));
+                outState.putParcelable(KEY_PATTERN_CHOICE, mChosenPattern);
             }
 
-            if (mCurrentPattern != null) {
-                outState.putByteArray(KEY_CURRENT_PATTERN, mCurrentPattern);
+            if (mCurrentCredential != null) {
+                outState.putParcelable(KEY_CURRENT_PATTERN, mCurrentCredential);
             }
         }
 
@@ -812,15 +814,18 @@
             final boolean required = getActivity().getIntent().getBooleanExtra(
                     EncryptionInterstitial.EXTRA_REQUIRE_PASSWORD, true);
             mSaveAndFinishWorker.start(mChooseLockSettingsHelper.utils(), required,
-                    mHasChallenge, mChallenge, mChosenPattern, mCurrentPattern, mUserId);
+                    mHasChallenge, mChallenge, mChosenPattern, mCurrentCredential, mUserId);
         }
 
         @Override
         public void onChosenLockSaveFinished(boolean wasSecureBefore, Intent resultData) {
             getActivity().setResult(RESULT_FINISHED, resultData);
 
-            if (mCurrentPattern != null) {
-                Arrays.fill(mCurrentPattern, (byte) 0);
+            if (mChosenPattern != null) {
+                mChosenPattern.zeroize();
+            }
+            if (mCurrentCredential != null) {
+                mCurrentCredential.zeroize();
             }
 
             if (!wasSecureBefore) {
@@ -835,16 +840,17 @@
 
     public static class SaveAndFinishWorker extends SaveChosenLockWorkerBase {
 
-        private List<LockPatternView.Cell> mChosenPattern;
-        private byte[] mCurrentPattern;
+        private LockscreenCredential mChosenPattern;
+        private LockscreenCredential mCurrentCredential;
         private boolean mLockVirgin;
 
-        public void start(LockPatternUtils utils, boolean credentialRequired,
-                boolean hasChallenge, long challenge,
-                List<LockPatternView.Cell> chosenPattern, byte[] currentPattern, int userId) {
+        public void start(LockPatternUtils utils, boolean credentialRequired, boolean hasChallenge,
+                long challenge, LockscreenCredential chosenPattern,
+                LockscreenCredential currentCredential, int userId) {
             prepare(utils, credentialRequired, hasChallenge, challenge, userId);
 
-            mCurrentPattern = currentPattern;
+            mCurrentCredential = currentCredential != null ? currentCredential
+                    : LockscreenCredential.createNone();
             mChosenPattern = chosenPattern;
             mUserId = userId;
 
@@ -856,12 +862,13 @@
         @Override
         protected Pair<Boolean, Intent> saveAndVerifyInBackground() {
             final int userId = mUserId;
-            final boolean success = mUtils.saveLockPattern(mChosenPattern, mCurrentPattern, userId);
+            final boolean success = mUtils.setLockCredential(mChosenPattern, mCurrentCredential,
+                    userId);
             Intent result = null;
             if (success && mHasChallenge) {
                 byte[] token;
                 try {
-                    token = mUtils.verifyPattern(mChosenPattern, mChallenge, userId);
+                    token = mUtils.verifyCredential(mChosenPattern, mChallenge, userId);
                 } catch (RequestThrottledException e) {
                     token = null;
                 }
diff --git a/src/com/android/settings/password/ConfirmLockPassword.java b/src/com/android/settings/password/ConfirmLockPassword.java
index 5c891cd..13d3749 100644
--- a/src/com/android/settings/password/ConfirmLockPassword.java
+++ b/src/com/android/settings/password/ConfirmLockPassword.java
@@ -27,6 +27,7 @@
 import android.os.SystemClock;
 import android.os.UserManager;
 import android.os.storage.StorageManager;
+import android.text.Editable;
 import android.text.InputType;
 import android.text.TextUtils;
 import android.view.KeyEvent;
@@ -44,6 +45,7 @@
 
 import com.android.internal.widget.LockPatternChecker;
 import com.android.internal.widget.LockPatternUtils;
+import com.android.internal.widget.LockscreenCredential;
 import com.android.internal.widget.TextViewInputDisabler;
 import com.android.settings.R;
 import com.android.settings.widget.ImeAwareEditText;
@@ -332,10 +334,13 @@
             }
 
             // TODO(b/120484642): This is a point of entry for passwords from the UI
-            final byte[] pin = LockPatternUtils.charSequenceToByteArray(mPasswordEntry.getText());
-            if (pin == null || pin.length == 0) {
+            final Editable passwordText = mPasswordEntry.getText();
+            if (TextUtils.isEmpty(passwordText)) {
                 return;
             }
+            final LockscreenCredential credential =
+                    mIsAlpha ? LockscreenCredential.createPassword(passwordText)
+                    : LockscreenCredential.createPin(passwordText);
 
             mPasswordEntryInputDisabler.setInputEnabled(false);
             final boolean verifyChallenge = getActivity().getIntent().getBooleanExtra(
@@ -344,11 +349,11 @@
             Intent intent = new Intent();
             if (verifyChallenge)  {
                 if (isInternalActivity()) {
-                    startVerifyPassword(pin, intent);
+                    startVerifyPassword(credential, intent);
                     return;
                 }
             } else {
-                startCheckPassword(pin, intent);
+                startCheckPassword(credential, intent);
                 return;
             }
 
@@ -359,7 +364,7 @@
             return getActivity() instanceof ConfirmLockPassword.InternalActivity;
         }
 
-        private void startVerifyPassword(final byte[] pin, final Intent intent) {
+        private void startVerifyPassword(LockscreenCredential credential, final Intent intent) {
             long challenge = getActivity().getIntent().getLongExtra(
                     ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE, 0);
             final int localEffectiveUserId = mEffectiveUserId;
@@ -383,29 +388,32 @@
                         }
             };
             mPendingLockCheck = (localEffectiveUserId == localUserId)
-                    ? LockPatternChecker.verifyPassword(
-                            mLockPatternUtils, pin, challenge, localUserId, onVerifyCallback)
+                    ? LockPatternChecker.verifyCredential(
+                            mLockPatternUtils, credential, challenge, localUserId, onVerifyCallback)
                     : LockPatternChecker.verifyTiedProfileChallenge(
-                            mLockPatternUtils, pin, false, challenge, localUserId,
+                            mLockPatternUtils, credential, challenge, localUserId,
                             onVerifyCallback);
         }
 
-        private void startCheckPassword(final byte[] pin, final Intent intent) {
+        private void startCheckPassword(final LockscreenCredential credential,
+                final Intent intent) {
             final int localEffectiveUserId = mEffectiveUserId;
-            mPendingLockCheck = LockPatternChecker.checkPassword(
+            mPendingLockCheck = LockPatternChecker.checkCredential(
                     mLockPatternUtils,
-                    pin,
+                    credential,
                     localEffectiveUserId,
                     new LockPatternChecker.OnCheckCallback() {
                         @Override
                         public void onChecked(boolean matched, int timeoutMs) {
                             mPendingLockCheck = null;
                             if (matched && isInternalActivity() && mReturnCredentials) {
+                                // TODO: get rid of EXTRA_KEY_TYPE, since EXTRA_KEY_PASSWORD already
+                                // distinguishes beteween PIN and password.
                                 intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_TYPE,
                                                 mIsAlpha ? StorageManager.CRYPT_TYPE_PASSWORD
                                                          : StorageManager.CRYPT_TYPE_PIN);
                                 intent.putExtra(
-                                        ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD, pin);
+                                        ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD, credential);
                             }
                             mCredentialCheckResultTracker.setResult(matched, intent, timeoutMs,
                                     localEffectiveUserId);
diff --git a/src/com/android/settings/password/ConfirmLockPattern.java b/src/com/android/settings/password/ConfirmLockPattern.java
index b1d8e60..2f7557e 100644
--- a/src/com/android/settings/password/ConfirmLockPattern.java
+++ b/src/com/android/settings/password/ConfirmLockPattern.java
@@ -37,6 +37,7 @@
 import com.android.internal.widget.LockPatternUtils;
 import com.android.internal.widget.LockPatternView;
 import com.android.internal.widget.LockPatternView.Cell;
+import com.android.internal.widget.LockscreenCredential;
 import com.android.settings.R;
 import com.android.settingslib.animation.AppearAnimationCreator;
 import com.android.settingslib.animation.AppearAnimationUtils;
@@ -401,14 +402,16 @@
 
                 final boolean verifyChallenge = getActivity().getIntent().getBooleanExtra(
                         ChooseLockSettingsHelper.EXTRA_KEY_HAS_CHALLENGE, false);
+                final LockscreenCredential credential = LockscreenCredential.createPattern(pattern);
+                //TODO: how to sanitize this?
                 Intent intent = new Intent();
                 if (verifyChallenge) {
                     if (isInternalActivity()) {
-                        startVerifyPattern(pattern, intent);
+                        startVerifyPattern(credential, intent);
                         return;
                     }
                 } else {
-                    startCheckPattern(pattern, intent);
+                    startCheckPattern(credential, intent);
                     return;
                 }
 
@@ -419,7 +422,7 @@
                 return getActivity() instanceof ConfirmLockPattern.InternalActivity;
             }
 
-            private void startVerifyPattern(final List<LockPatternView.Cell> pattern,
+            private void startVerifyPattern(final LockscreenCredential pattern,
                     final Intent intent) {
                 final int localEffectiveUserId = mEffectiveUserId;
                 final int localUserId = mUserId;
@@ -444,15 +447,15 @@
                         }
                     };
                 mPendingLockCheck = (localEffectiveUserId == localUserId)
-                        ? LockPatternChecker.verifyPattern(
+                        ? LockPatternChecker.verifyCredential(
                                 mLockPatternUtils, pattern, challenge, localUserId,
                                 onVerifyCallback)
                         : LockPatternChecker.verifyTiedProfileChallenge(
-                                mLockPatternUtils, LockPatternUtils.patternToByteArray(pattern),
-                                true, challenge, localUserId, onVerifyCallback);
+                                mLockPatternUtils, pattern,
+                                challenge, localUserId, onVerifyCallback);
             }
 
-            private void startCheckPattern(final List<LockPatternView.Cell> pattern,
+            private void startCheckPattern(final LockscreenCredential pattern,
                     final Intent intent) {
                 if (pattern.size() < LockPatternUtils.MIN_PATTERN_REGISTER_FAIL) {
                     // Pattern size is less than the minimum, do not count it as an fail attempt.
@@ -461,7 +464,7 @@
                 }
 
                 final int localEffectiveUserId = mEffectiveUserId;
-                mPendingLockCheck = LockPatternChecker.checkPattern(
+                mPendingLockCheck = LockPatternChecker.checkCredential(
                         mLockPatternUtils,
                         pattern,
                         localEffectiveUserId,
@@ -473,7 +476,7 @@
                                     intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_TYPE,
                                                     StorageManager.CRYPT_TYPE_PATTERN);
                                     intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD,
-                                                    LockPatternUtils.patternToByteArray(pattern));
+                                                    pattern);
                                 }
                                 mCredentialCheckResultTracker.setResult(matched, intent, timeoutMs,
                                         localEffectiveUserId);
diff --git a/src/com/android/settings/password/ManagedLockPasswordProvider.java b/src/com/android/settings/password/ManagedLockPasswordProvider.java
index 5006926..ac8a73a 100644
--- a/src/com/android/settings/password/ManagedLockPasswordProvider.java
+++ b/src/com/android/settings/password/ManagedLockPasswordProvider.java
@@ -19,6 +19,8 @@
 import android.content.Context;
 import android.content.Intent;
 
+import com.android.internal.widget.LockscreenCredential;
+
 /**
  * Helper for handling managed passwords in security settings UI.
  * It provides resources that should be shown in settings UI when lock password quality is set to
@@ -59,7 +61,7 @@
      * @param password Current lock password.
      * @return Intent that should update lock password to a managed password.
      */
-    Intent createIntent(boolean requirePasswordToDecrypt, byte[] password) {
+    Intent createIntent(boolean requirePasswordToDecrypt, LockscreenCredential password) {
         return null;
     }
 }
diff --git a/src/com/android/settings/security/CryptKeeperSettings.java b/src/com/android/settings/security/CryptKeeperSettings.java
index 6555f56..aa47b2b 100644
--- a/src/com/android/settings/security/CryptKeeperSettings.java
+++ b/src/com/android/settings/security/CryptKeeperSettings.java
@@ -37,6 +37,7 @@
 import androidx.appcompat.app.AlertDialog;
 import androidx.preference.Preference;
 
+import com.android.internal.widget.LockscreenCredential;
 import com.android.settings.CryptKeeperConfirm;
 import com.android.settings.R;
 import com.android.settings.SettingsActivity;
@@ -193,9 +194,10 @@
         // confirmation prompt; otherwise, go back to the initial state.
         if (resultCode == Activity.RESULT_OK && data != null) {
             int type = data.getIntExtra(ChooseLockSettingsHelper.EXTRA_KEY_TYPE, -1);
-            byte[] password = data.getByteArrayExtra(ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD);
-            if (!(password == null || password.length == 0)) {
-                showFinalConfirmation(type, password);
+            LockscreenCredential password = data.getParcelableExtra(
+                    ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD);
+            if (password != null && !password.isNone()) {
+                showFinalConfirmation(type, password.getCredential());
             }
         }
     }
diff --git a/src/com/android/settings/security/LockUnificationPreferenceController.java b/src/com/android/settings/security/LockUnificationPreferenceController.java
index bf374de..4bac601 100644
--- a/src/com/android/settings/security/LockUnificationPreferenceController.java
+++ b/src/com/android/settings/security/LockUnificationPreferenceController.java
@@ -32,6 +32,7 @@
 import androidx.preference.PreferenceScreen;
 
 import com.android.internal.widget.LockPatternUtils;
+import com.android.internal.widget.LockscreenCredential;
 import com.android.settings.R;
 import com.android.settings.Utils;
 import com.android.settings.core.PreferenceControllerMixin;
@@ -70,8 +71,8 @@
     private RestrictedSwitchPreference mUnifyProfile;
 
 
-    private byte[] mCurrentDevicePassword;
-    private byte[] mCurrentProfilePassword;
+    private LockscreenCredential mCurrentDevicePassword;
+    private LockscreenCredential mCurrentProfilePassword;
     private boolean mKeepDeviceLock;
 
     @Override
@@ -89,6 +90,8 @@
                 .getSecurityFeatureProvider()
                 .getLockPatternUtils(context);
         mProfileUserId = Utils.getManagedProfileId(mUm, MY_USER_ID);
+        mCurrentDevicePassword = LockscreenCredential.createNone();
+        mCurrentProfilePassword = LockscreenCredential.createNone();
     }
 
     @Override
@@ -151,13 +154,13 @@
         } else if (requestCode == UNIFY_LOCK_CONFIRM_DEVICE_REQUEST
                 && resultCode == Activity.RESULT_OK) {
             mCurrentDevicePassword =
-                    data.getByteArrayExtra(ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD);
+                    data.getParcelableExtra(ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD);
             launchConfirmProfileLock();
             return true;
         } else if (requestCode == UNIFY_LOCK_CONFIRM_PROFILE_REQUEST
                 && resultCode == Activity.RESULT_OK) {
             mCurrentProfilePassword =
-                    data.getByteArrayExtra(ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD);
+                    data.getParcelableExtra(ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD);
             unifyLocks();
             return true;
         }
@@ -221,17 +224,8 @@
     }
 
     private void unifyKeepingWorkLock() {
-        final int profileQuality =
-                mLockPatternUtils.getKeyguardStoredPasswordQuality(mProfileUserId);
-        // PASSWORD_QUALITY_SOMETHING means pattern, everything above means PIN/password.
-        if (profileQuality == DevicePolicyManager.PASSWORD_QUALITY_SOMETHING) {
-            mLockPatternUtils.saveLockPattern(
-                    LockPatternUtils.byteArrayToPattern(mCurrentProfilePassword),
-                    mCurrentDevicePassword, MY_USER_ID);
-        } else {
-            mLockPatternUtils.saveLockPassword(
-                    mCurrentProfilePassword, mCurrentDevicePassword, profileQuality, MY_USER_ID);
-        }
+        mLockPatternUtils.setLockCredential(
+                mCurrentProfilePassword, mCurrentDevicePassword, MY_USER_ID);
         mLockPatternUtils.setSeparateProfileChallengeEnabled(mProfileUserId, false,
                 mCurrentProfilePassword);
         final boolean profilePatternVisibility =