Refactor LockSettingsService to unify the handling of pattern and password

Also fix LockSettingsStorageTests. More unit tests on LockSettingsService
to be added in the next CL.

Bug: 33126408
Test: runtest frameworks-services -c com.android.server.LockSettingsStorageTests
Change-Id: I0f143b26fed1d5ae122fba3b57bd39c7793ad8d9
diff --git a/services/core/java/com/android/server/LockSettingsService.java b/services/core/java/com/android/server/LockSettingsService.java
index a5552b8..51503c0 100644
--- a/services/core/java/com/android/server/LockSettingsService.java
+++ b/services/core/java/com/android/server/LockSettingsService.java
@@ -16,6 +16,11 @@
 
 package com.android.server;
 
+import static android.Manifest.permission.ACCESS_KEYGUARD_SECURE_STORAGE;
+import static android.Manifest.permission.READ_CONTACTS;
+import static android.content.Context.KEYGUARD_SERVICE;
+import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_LOCKOUT;
+
 import android.annotation.UserIdInt;
 import android.app.ActivityManager;
 import android.app.KeyguardManager;
@@ -35,31 +40,23 @@
 import android.content.pm.PackageManager;
 import android.content.pm.UserInfo;
 import android.content.res.Resources;
-
-import static android.Manifest.permission.ACCESS_KEYGUARD_SECURE_STORAGE;
-import static android.content.Context.KEYGUARD_SERVICE;
-import static android.content.Context.USER_SERVICE;
-import static android.Manifest.permission.READ_CONTACTS;
-import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_LOCKOUT;
-
 import android.database.sqlite.SQLiteDatabase;
 import android.os.Binder;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.IProgressListener;
-import android.os.Parcel;
 import android.os.Process;
 import android.os.RemoteException;
 import android.os.ResultReceiver;
-import android.os.ShellCallback;
-import android.os.storage.IStorageManager;
-import android.os.storage.StorageManager;
 import android.os.ServiceManager;
+import android.os.ShellCallback;
 import android.os.StrictMode;
 import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.os.UserManager;
+import android.os.storage.IStorageManager;
+import android.os.storage.StorageManager;
 import android.provider.Settings;
 import android.provider.Settings.Secure;
 import android.provider.Settings.SettingNotFoundException;
@@ -109,20 +106,21 @@
 import javax.crypto.spec.GCMParameterSpec;
 
 /**
- * Keeps the lock pattern/password data and related settings for each user.
- * Used by LockPatternUtils. Needs to be a service because Settings app also needs
- * to be able to save lockscreen information for secondary users.
+ * Keeps the lock pattern/password data and related settings for each user. Used by
+ * LockPatternUtils. Needs to be a service because Settings app also needs to be able to save
+ * lockscreen information for secondary users.
+ *
  * @hide
  */
 public class LockSettingsService extends ILockSettings.Stub {
     private static final String TAG = "LockSettingsService";
     private static final String PERMISSION = ACCESS_KEYGUARD_SECURE_STORAGE;
-    private static final Intent ACTION_NULL; // hack to ensure notification shows the bouncer
     private static final int FBE_ENCRYPTED_NOTIFICATION = 0;
     private static final boolean DEBUG = false;
 
     private static final int PROFILE_KEY_IV_SIZE = 12;
     private static final String SEPARATE_PROFILE_CHALLENGE_KEY = "lockscreen.profilechallenge";
+
     private final Object mSeparateChallengeLock = new Object();
 
     private final Context mContext;
@@ -142,21 +140,9 @@
     /**
      * The UIDs that are used for system credential storage in keystore.
      */
-    private static final int[] SYSTEM_CREDENTIAL_UIDS = {Process.WIFI_UID, Process.VPN_UID,
-        Process.ROOT_UID, Process.SYSTEM_UID};
-
-    static {
-        // Just launch the home screen, which happens anyway
-        ACTION_NULL = new Intent(Intent.ACTION_MAIN);
-        ACTION_NULL.addCategory(Intent.CATEGORY_HOME);
-    }
-
-    private interface CredentialUtil {
-        void setCredential(String credential, String savedCredential, int userId)
-                throws RemoteException;
-        byte[] toHash(String credential, int userId);
-        String adjustForKeystore(String credential);
-    }
+    private static final int[] SYSTEM_CREDENTIAL_UIDS = {
+            Process.WIFI_UID, Process.VPN_UID,
+            Process.ROOT_UID, Process.SYSTEM_UID };
 
     // This class manages life cycle events for encrypted users on File Based Encryption (FBE)
     // devices. The most basic of these is to show/hide notifications about missing features until
@@ -258,7 +244,8 @@
         try {
             randomLockSeed = SecureRandom.getInstance("SHA1PRNG").generateSeed(40);
             String newPassword = String.valueOf(HexEncoding.encode(randomLockSeed));
-            setLockPasswordInternal(newPassword, managedUserPassword, managedUserId);
+            setLockCredentialInternal(newPassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD,
+                    managedUserPassword, managedUserId);
             // We store a private credential for the managed user that's unlocked by the primary
             // account holder's credential. As such, the user will never be prompted to enter this
             // password directly, so we always store a password.
@@ -306,8 +293,8 @@
     }
 
     /**
-     * If the account is credential-encrypted, show notification requesting the user to unlock
-     * the device.
+     * If the account is credential-encrypted, show notification requesting the user to unlock the
+     * device.
      */
     private void maybeShowEncryptionNotificationForUser(@UserIdInt int userId) {
         final UserInfo user = mUserManager.getUserInfo(userId);
@@ -342,19 +329,21 @@
                 com.android.internal.R.string.profile_encrypted_detail);
 
         final KeyguardManager km = (KeyguardManager) mContext.getSystemService(KEYGUARD_SERVICE);
-        final Intent unlockIntent = km.createConfirmDeviceCredentialIntent(null, null, user.getIdentifier());
+        final Intent unlockIntent = km.createConfirmDeviceCredentialIntent(null, null,
+                user.getIdentifier());
         if (unlockIntent == null) {
             return;
         }
-        unlockIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
+        unlockIntent.setFlags(
+                Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
         PendingIntent intent = PendingIntent.getActivity(mContext, 0, unlockIntent,
                 PendingIntent.FLAG_UPDATE_CURRENT);
 
         showEncryptionNotification(user, title, message, detail, intent);
     }
 
-    private void showEncryptionNotification(UserHandle user, CharSequence title, CharSequence message,
-            CharSequence detail, PendingIntent intent) {
+    private void showEncryptionNotification(UserHandle user, CharSequence title,
+            CharSequence message, CharSequence detail, PendingIntent intent) {
         if (DEBUG) Slog.v(TAG, "showing encryption notification, user: " + user.getIdentifier());
 
         // Suppress all notifications on non-FBE devices for now
@@ -365,7 +354,7 @@
                 .setWhen(0)
                 .setOngoing(true)
                 .setTicker(title)
-                .setDefaults(0)  // please be quiet
+                .setDefaults(0) // please be quiet
                 .setPriority(Notification.PRIORITY_MAX)
                 .setColor(mContext.getColor(
                         com.android.internal.R.color.system_notification_accent_color))
@@ -379,7 +368,7 @@
     }
 
     private void hideEncryptionNotification(UserHandle userHandle) {
-        if (DEBUG) Slog.v(TAG, "hide encryption notification, user: "+ userHandle.getIdentifier());
+        if (DEBUG) Slog.v(TAG, "hide encryption notification, user: " + userHandle.getIdentifier());
         mNotificationManager.cancelAsUser(null, FBE_ENCRYPTED_NOTIFICATION, userHandle);
     }
 
@@ -491,7 +480,7 @@
                         Settings.Secure.putStringForUser(cr, OWNER_INFO, "", userId);
                     }
 
-                    // Migrate owner info enabled.  Note there was a bug where older platforms only
+                    // Migrate owner info enabled. Note there was a bug where older platforms only
                     // stored this value if the checkbox was toggled at least once. The code detects
                     // this case by handling the exception.
                     final String OWNER_INFO_ENABLED = Secure.LOCK_SCREEN_OWNER_INFO_ENABLED;
@@ -788,8 +777,9 @@
 
     private void unlockChildProfile(int profileHandle) throws RemoteException {
         try {
-            doVerifyPassword(getDecryptedPasswordForTiedProfile(profileHandle), false,
-                    0 /* no challenge */, profileHandle, null /* progressCallback */);
+            doVerifyCredential(getDecryptedPasswordForTiedProfile(profileHandle),
+                    LockPatternUtils.CREDENTIAL_TYPE_PASSWORD,
+                    false, 0 /* no challenge */, profileHandle, null /* progressCallback */);
         } catch (UnrecoverableKeyException | InvalidKeyException | KeyStoreException
                 | NoSuchAlgorithmException | NoSuchPaddingException
                 | InvalidAlgorithmParameterException | IllegalBlockSizeException
@@ -852,35 +842,13 @@
     }
 
     private byte[] getCurrentHandle(int userId) {
-        CredentialHash credential;
-        byte[] currentHandle;
-
-        int currentHandleType = mStorage.getStoredCredentialType(userId);
-        switch (currentHandleType) {
-            case CredentialHash.TYPE_PATTERN:
-                credential = mStorage.readPatternHash(userId);
-                currentHandle = credential != null
-                        ? credential.hash
-                        : null;
-                break;
-            case CredentialHash.TYPE_PASSWORD:
-                credential = mStorage.readPasswordHash(userId);
-                currentHandle = credential != null
-                        ? credential.hash
-                        : null;
-                break;
-            case CredentialHash.TYPE_NONE:
-            default:
-                currentHandle = null;
-                break;
-        }
+        CredentialHash credential = mStorage.readCredentialHash(userId);
 
         // sanity check
-        if (currentHandleType != CredentialHash.TYPE_NONE && currentHandle == null) {
-            Slog.e(TAG, "Stored handle type [" + currentHandleType + "] but no handle available");
+        if (credential.type != LockPatternUtils.CREDENTIAL_TYPE_NONE && credential.hash == null) {
+            Slog.e(TAG, "Stored handle type [" + credential.type + "] but no handle available");
         }
-
-        return currentHandle;
+        return credential.hash;
     }
 
     private void onUserLockChanged(int userId) throws RemoteException {
@@ -902,7 +870,7 @@
                 } else {
                     clearUserKeyProtection(managedUserId);
                     getGateKeeperService().clearSecureUserId(managedUserId);
-                    mStorage.writePatternHash(null, managedUserId);
+                    mStorage.writeCredentialHash(CredentialHash.createEmptyHash(), managedUserId);
                     setKeystorePassword(null, managedUserId);
                     fixateNewestUserKeyAuth(managedUserId);
                     mStorage.removeChildProfileLock(managedUserId);
@@ -923,97 +891,37 @@
     }
 
     // This method should be called by LockPatternUtil only, all internal methods in this class
-    // should call setLockPatternInternal.
+    // should call setLockCredentialInternal.
     @Override
-    public void setLockPattern(String pattern, String savedCredential, int userId)
+    public void setLockCredential(String credential, int type, String savedCredential, int userId)
             throws RemoteException {
         checkWritePermission(userId);
         synchronized (mSeparateChallengeLock) {
-            setLockPatternInternal(pattern, savedCredential, userId);
+            setLockCredentialInternal(credential, type, savedCredential, userId);
             setSeparateProfileChallengeEnabled(userId, true, null);
             notifyPasswordChanged(userId);
         }
     }
 
-    private void setLockPatternInternal(String pattern, String savedCredential, int userId)
-            throws RemoteException {
+    private void setLockCredentialInternal(String credential, int credentialType,
+            String savedCredential, int userId) throws RemoteException {
         byte[] currentHandle = getCurrentHandle(userId);
-
-        if (pattern == null) {
+        if (credentialType == LockPatternUtils.CREDENTIAL_TYPE_NONE) {
+            if (credential != null) {
+                Slog.wtf(TAG, "CredentialType is none, but credential is non-null.");
+            }
             clearUserKeyProtection(userId);
             getGateKeeperService().clearSecureUserId(userId);
-            mStorage.writePatternHash(null, userId);
+            mStorage.writeCredentialHash(CredentialHash.createEmptyHash(), userId);
             setKeystorePassword(null, userId);
             fixateNewestUserKeyAuth(userId);
             onUserLockChanged(userId);
             notifyActivePasswordMetricsAvailable(null, userId);
             return;
         }
-
-        if (isManagedProfileWithUnifiedLock(userId)) {
-            // get credential from keystore when managed profile has unified lock
-            try {
-                savedCredential = getDecryptedPasswordForTiedProfile(userId);
-            } catch (UnrecoverableKeyException | InvalidKeyException | KeyStoreException
-                    | NoSuchAlgorithmException | NoSuchPaddingException
-                    | InvalidAlgorithmParameterException | IllegalBlockSizeException
-                    | BadPaddingException | CertificateException | IOException e) {
-                if (e instanceof FileNotFoundException) {
-                    Slog.i(TAG, "Child profile key not found");
-                } else {
-                    Slog.e(TAG, "Failed to decrypt child profile key", e);
-                }
-            }
-        } else {
-            if (currentHandle == null) {
-                if (savedCredential != null) {
-                    Slog.w(TAG, "Saved credential provided, but none stored");
-                }
-                savedCredential = null;
-            }
+        if (credential == null) {
+            throw new RemoteException("Null credential with mismatched credential type");
         }
-
-        byte[] enrolledHandle = enrollCredential(currentHandle, savedCredential, pattern, userId);
-        if (enrolledHandle != null) {
-            CredentialHash willStore
-                = new CredentialHash(enrolledHandle, CredentialHash.VERSION_GATEKEEPER);
-            setUserKeyProtection(userId, pattern,
-                doVerifyPattern(pattern, willStore, true, 0, userId, null /* progressCallback */));
-            mStorage.writePatternHash(enrolledHandle, userId);
-            fixateNewestUserKeyAuth(userId);
-            onUserLockChanged(userId);
-        } else {
-            throw new RemoteException("Failed to enroll pattern");
-        }
-    }
-
-    // This method should be called by LockPatternUtil only, all internal methods in this class
-    // should call setLockPasswordInternal.
-    @Override
-    public void setLockPassword(String password, String savedCredential, int userId)
-            throws RemoteException {
-        checkWritePermission(userId);
-        synchronized (mSeparateChallengeLock) {
-            setLockPasswordInternal(password, savedCredential, userId);
-            setSeparateProfileChallengeEnabled(userId, true, null);
-            notifyPasswordChanged(userId);
-        }
-    }
-
-    private void setLockPasswordInternal(String password, String savedCredential, int userId)
-            throws RemoteException {
-        byte[] currentHandle = getCurrentHandle(userId);
-        if (password == null) {
-            clearUserKeyProtection(userId);
-            getGateKeeperService().clearSecureUserId(userId);
-            mStorage.writePasswordHash(null, userId);
-            setKeystorePassword(null, userId);
-            fixateNewestUserKeyAuth(userId);
-            onUserLockChanged(userId);
-            notifyActivePasswordMetricsAvailable(null, userId);
-            return;
-        }
-
         if (isManagedProfileWithUnifiedLock(userId)) {
             // get credential from keystore when managed profile has unified lock
             try {
@@ -1035,18 +943,21 @@
             }
         }
 
-        byte[] enrolledHandle = enrollCredential(currentHandle, savedCredential, password, userId);
+        byte[] enrolledHandle = enrollCredential(currentHandle, savedCredential, credential,
+                userId);
         if (enrolledHandle != null) {
-            CredentialHash willStore
-                = new CredentialHash(enrolledHandle, CredentialHash.VERSION_GATEKEEPER);
-            setUserKeyProtection(userId, password,
-                doVerifyPassword(password, willStore, true, 0, userId,
-                        null /* progressCallback */));
-            mStorage.writePasswordHash(enrolledHandle, userId);
+            CredentialHash willStore = CredentialHash.create(enrolledHandle, credentialType);
+            mStorage.writeCredentialHash(willStore, userId);
+            // Refresh the auth token
+            setUserKeyProtection(userId, credential,
+                    doVerifyCredential(credential, credentialType, true, 0, userId,
+                            null /* progressCallback */));
             fixateNewestUserKeyAuth(userId);
             onUserLockChanged(userId);
         } else {
-            throw new RemoteException("Failed to enroll password");
+            throw new RemoteException("Failed to enroll " +
+                    (credentialType == LockPatternUtils.CREDENTIAL_TYPE_PASSWORD ? "password"
+                            : "pattern"));
         }
     }
 
@@ -1143,7 +1054,7 @@
         }
         if (vcr.getResponseCode() != VerifyCredentialResponse.RESPONSE_OK) {
             throw new RemoteException("Non-OK response verifying a credential we just set: "
-                + vcr.getResponseCode());
+                    + vcr.getResponseCode());
         }
         byte[] token = vcr.getPayload();
         if (token == null) {
@@ -1240,90 +1151,61 @@
     }
 
     @Override
-    public VerifyCredentialResponse checkPattern(String pattern, int userId,
+    public VerifyCredentialResponse checkCredential(String credential, int type, int userId,
             ICheckCredentialProgressCallback progressCallback) throws RemoteException {
-        return doVerifyPattern(pattern, false, 0, userId, progressCallback);
+        checkPasswordReadPermission(userId);
+        return doVerifyCredential(credential, type, false, 0, userId, progressCallback);
     }
 
     @Override
-    public VerifyCredentialResponse verifyPattern(String pattern, long challenge, int userId)
-            throws RemoteException {
-        return doVerifyPattern(pattern, true, challenge, userId, null /* progressCallback */);
+    public VerifyCredentialResponse verifyCredential(String credential, int type, long challenge,
+            int userId) throws RemoteException {
+        checkPasswordReadPermission(userId);
+        return doVerifyCredential(credential, type, true, challenge, userId,
+                null /* progressCallback */);
     }
 
-    private VerifyCredentialResponse doVerifyPattern(String pattern, boolean hasChallenge,
-            long challenge, int userId, ICheckCredentialProgressCallback progressCallback)
-            throws RemoteException {
-       checkPasswordReadPermission(userId);
-       if (TextUtils.isEmpty(pattern)) {
-           throw new IllegalArgumentException("Pattern can't be null or empty");
-       }
-       CredentialHash storedHash = mStorage.readPatternHash(userId);
-       return doVerifyPattern(pattern, storedHash, hasChallenge, challenge, userId,
-               progressCallback);
-    }
-
-    private VerifyCredentialResponse doVerifyPattern(String pattern, CredentialHash storedHash,
+    /**
+     * Verify user credential and unlock the user. Fix pattern bug by deprecating the old base zero
+     * format.
+     */
+    private VerifyCredentialResponse doVerifyCredential(String credential, int credentialType,
             boolean hasChallenge, long challenge, int userId,
             ICheckCredentialProgressCallback progressCallback) throws RemoteException {
+        if (TextUtils.isEmpty(credential)) {
+            throw new IllegalArgumentException("Credential can't be null or empty");
+        }
 
-       if (TextUtils.isEmpty(pattern)) {
-           throw new IllegalArgumentException("Pattern can't be null or empty");
-       }
-       boolean shouldReEnrollBaseZero = storedHash != null && storedHash.isBaseZeroPattern;
+        CredentialHash storedHash = mStorage.readCredentialHash(userId);
+        if (storedHash.type != credentialType) {
+            Slog.wtf(TAG, "doVerifyCredential type mismatch with stored credential??"
+                    + " stored: " + storedHash.type + " passed in: " + credentialType);
+            return VerifyCredentialResponse.ERROR;
+        }
 
-       String patternToVerify;
-       if (shouldReEnrollBaseZero) {
-           patternToVerify = LockPatternUtils.patternStringToBaseZero(pattern);
-       } else {
-           patternToVerify = pattern;
-       }
+        boolean shouldReEnrollBaseZero = storedHash.type == LockPatternUtils.CREDENTIAL_TYPE_PATTERN
+                && storedHash.isBaseZeroPattern;
 
-       VerifyCredentialResponse response = verifyCredential(userId, storedHash, patternToVerify,
-               hasChallenge, challenge,
-               new CredentialUtil() {
-                   @Override
-                   public void setCredential(String pattern, String oldPattern, int userId)
-                           throws RemoteException {
-                        setLockPatternInternal(pattern, oldPattern, userId);
-                   }
+        String credentialToVerify;
+        if (shouldReEnrollBaseZero) {
+            credentialToVerify = LockPatternUtils.patternStringToBaseZero(credential);
+        } else {
+            credentialToVerify = credential;
+        }
 
-                   @Override
-                   public byte[] toHash(String pattern, int userId) {
-                       return LockPatternUtils.patternToHash(
-                               LockPatternUtils.stringToPattern(pattern));
-                   }
+        VerifyCredentialResponse response = verifyCredential(userId, storedHash, credentialToVerify,
+                hasChallenge, challenge, progressCallback);
 
-                   @Override
-                   public String adjustForKeystore(String pattern) {
-                       return LockPatternUtils.patternStringToBaseZero(pattern);
-                   }
-               },
-               progressCallback
-       );
+        if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_OK
+                && shouldReEnrollBaseZero) {
+            setLockCredentialInternal(credential, storedHash.type, credentialToVerify, userId);
+        }
 
-       if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_OK
-               && shouldReEnrollBaseZero) {
-            setLockPatternInternal(pattern, patternToVerify, userId);
-       }
-
-       return response;
+        return response;
     }
 
     @Override
-    public VerifyCredentialResponse checkPassword(String password, int userId,
-            ICheckCredentialProgressCallback progressCallback) throws RemoteException {
-        return doVerifyPassword(password, false, 0, userId, progressCallback);
-    }
-
-    @Override
-    public VerifyCredentialResponse verifyPassword(String password, long challenge, int userId)
-            throws RemoteException {
-        return doVerifyPassword(password, true, challenge, userId, null /* progressCallback */);
-    }
-
-    @Override
-    public VerifyCredentialResponse verifyTiedProfileChallenge(String password, boolean isPattern,
+    public VerifyCredentialResponse verifyTiedProfileChallenge(String credential, int type,
             long challenge, int userId) throws RemoteException {
         checkPasswordReadPermission(userId);
         if (!isManagedProfileWithUnifiedLock(userId)) {
@@ -1331,11 +1213,13 @@
         }
         final int parentProfileId = mUserManager.getProfileParent(userId).id;
         // Unlock parent by using parent's challenge
-        final VerifyCredentialResponse parentResponse = isPattern
-                ? doVerifyPattern(password, true, challenge, parentProfileId,
-                        null /* progressCallback */)
-                : doVerifyPassword(password, true, challenge, parentProfileId,
-                        null /* progressCallback */);
+        final VerifyCredentialResponse parentResponse = doVerifyCredential(
+                credential,
+                type,
+                true /* hasChallenge */,
+                challenge,
+                parentProfileId,
+                null /* progressCallback */);
         if (parentResponse.getResponseCode() != VerifyCredentialResponse.RESPONSE_OK) {
             // Failed, just return parent's response
             return parentResponse;
@@ -1343,7 +1227,9 @@
 
         try {
             // Unlock work profile, and work profile with unified lock must use password only
-            return doVerifyPassword(getDecryptedPasswordForTiedProfile(userId), true,
+            return doVerifyCredential(getDecryptedPasswordForTiedProfile(userId),
+                    LockPatternUtils.CREDENTIAL_TYPE_PASSWORD,
+                    true,
                     challenge,
                     userId, null /* progressCallback */);
         } catch (UnrecoverableKeyException | InvalidKeyException | KeyStoreException
@@ -1355,48 +1241,14 @@
         }
     }
 
-    private VerifyCredentialResponse doVerifyPassword(String password, boolean hasChallenge,
-            long challenge, int userId, ICheckCredentialProgressCallback progressCallback)
-            throws RemoteException {
-       checkPasswordReadPermission(userId);
-       if (TextUtils.isEmpty(password)) {
-           throw new IllegalArgumentException("Password can't be null or empty");
-       }
-       CredentialHash storedHash = mStorage.readPasswordHash(userId);
-       return doVerifyPassword(password, storedHash, hasChallenge, challenge, userId,
-               progressCallback);
-    }
-
-    private VerifyCredentialResponse doVerifyPassword(String password, CredentialHash storedHash,
-            boolean hasChallenge, long challenge, int userId,
-            ICheckCredentialProgressCallback progressCallback) throws RemoteException {
-       if (TextUtils.isEmpty(password)) {
-           throw new IllegalArgumentException("Password can't be null or empty");
-       }
-       return verifyCredential(userId, storedHash, password, hasChallenge, challenge,
-               new CredentialUtil() {
-                   @Override
-                   public void setCredential(String password, String oldPassword, int userId)
-                           throws RemoteException {
-                        setLockPasswordInternal(password, oldPassword, userId);
-                   }
-
-                   @Override
-                   public byte[] toHash(String password, int userId) {
-                       return mLockPatternUtils.passwordToHash(password, userId);
-                   }
-
-                   @Override
-                   public String adjustForKeystore(String password) {
-                       return password;
-                   }
-               }, progressCallback);
-    }
-
+    /**
+     * Lowest-level credential verification routine that talks to GateKeeper. If verification
+     * passes, unlock the corresponding user and keystore. Also handles the migration from legacy
+     * hash to GK.
+     */
     private VerifyCredentialResponse verifyCredential(int userId, CredentialHash storedHash,
-            String credential, boolean hasChallenge, long challenge, CredentialUtil credentialUtil,
-            ICheckCredentialProgressCallback progressCallback)
-                throws RemoteException {
+            String credential, boolean hasChallenge, long challenge,
+            ICheckCredentialProgressCallback progressCallback) throws RemoteException {
         if ((storedHash == null || storedHash.hash.length == 0) && TextUtils.isEmpty(credential)) {
             // don't need to pass empty credentials to GateKeeper
             return VerifyCredentialResponse.OK;
@@ -1411,10 +1263,18 @@
         StrictMode.noteDiskRead();
 
         if (storedHash.version == CredentialHash.VERSION_LEGACY) {
-            byte[] hash = credentialUtil.toHash(credential, userId);
+            final byte[] hash;
+            if (storedHash.type == LockPatternUtils.CREDENTIAL_TYPE_PATTERN) {
+                hash = LockPatternUtils.patternToHash(LockPatternUtils.stringToPattern(credential));
+            } else {
+                hash = mLockPatternUtils.passwordToHash(credential, userId);
+            }
             if (Arrays.equals(hash, storedHash.hash)) {
-                unlockKeystore(credentialUtil.adjustForKeystore(credential), userId);
-
+                if (storedHash.type == LockPatternUtils.CREDENTIAL_TYPE_PATTERN) {
+                    unlockKeystore(LockPatternUtils.patternStringToBaseZero(credential), userId);
+                } else {
+                    unlockKeystore(credential, userId);
+                }
                 // Users with legacy credentials don't have credential-backed
                 // FBE keys, so just pass through a fake token/secret
                 Slog.i(TAG, "Unlocking user with fake token: " + userId);
@@ -1422,7 +1282,7 @@
                 unlockUser(userId, fakeToken, fakeToken);
 
                 // migrate credential to GateKeeper
-                credentialUtil.setCredential(credential, null, userId);
+                setLockCredentialInternal(credential, storedHash.type, null, userId);
                 if (!hasChallenge) {
                     notifyActivePasswordMetricsAvailable(credential, userId);
                     return VerifyCredentialResponse.OK;
@@ -1441,7 +1301,7 @@
                 .verifyChallenge(userId, challenge, storedHash.hash, credential.getBytes());
         int responseCode = gateKeeperResponse.getResponseCode();
         if (responseCode == GateKeeperResponse.RESPONSE_RETRY) {
-             response = new VerifyCredentialResponse(gateKeeperResponse.getTimeout());
+            response = new VerifyCredentialResponse(gateKeeperResponse.getTimeout());
         } else if (responseCode == GateKeeperResponse.RESPONSE_OK) {
             byte[] token = gateKeeperResponse.getPayload();
             if (token == null) {
@@ -1458,7 +1318,6 @@
 
         if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_OK) {
 
-
             // credential has matched
 
             if (progressCallback != null) {
@@ -1468,7 +1327,7 @@
             unlockKeystore(credential, userId);
 
             Slog.i(TAG, "Unlocking user " + userId +
-                " with token length " + response.getPayload().length);
+                    " with token length " + response.getPayload().length);
             unlockUser(userId, response.getPayload(), secretFromCredential(credential));
 
             if (isManagedProfileWithSeparatedLock(userId)) {
@@ -1477,7 +1336,7 @@
                 trustManager.setDeviceLockedForUser(userId, false);
             }
             if (shouldReEnroll) {
-                credentialUtil.setCredential(credential, credential, userId);
+                setLockCredentialInternal(credential, storedHash.type, credential, userId);
             }
         } else if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_RETRY) {
             if (response.getTimeout() > 0) {
@@ -1550,8 +1409,9 @@
 
         try {
             if (mLockPatternUtils.isLockPatternEnabled(userId)) {
-                if (checkPattern(password, userId, null /* progressCallback */).getResponseCode()
-                        == GateKeeperResponse.RESPONSE_OK) {
+                if (checkCredential(password, LockPatternUtils.CREDENTIAL_TYPE_PATTERN, userId,
+                        null /* progressCallback */)
+                                .getResponseCode() == GateKeeperResponse.RESPONSE_OK) {
                     return true;
                 }
             }
@@ -1560,8 +1420,9 @@
 
         try {
             if (mLockPatternUtils.isLockPasswordEnabled(userId)) {
-                if (checkPassword(password, userId, null /* progressCallback */).getResponseCode()
-                        == GateKeeperResponse.RESPONSE_OK) {
+                if (checkCredential(password, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, userId,
+                        null /* progressCallback */)
+                                .getResponseCode() == GateKeeperResponse.RESPONSE_OK) {
                     return true;
                 }
             }
@@ -1581,7 +1442,7 @@
         try {
             final IGateKeeperService gk = getGateKeeperService();
             if (gk != null) {
-                    gk.clearSecureUserId(userId);
+                gk.clearSecureUserId(userId);
             }
         } catch (RemoteException ex) {
             Slog.w(TAG, "unable to clear GK secure user id");
@@ -1661,28 +1522,28 @@
     }
 
     private static final String[] VALID_SETTINGS = new String[] {
-        LockPatternUtils.LOCKOUT_PERMANENT_KEY,
-        LockPatternUtils.LOCKOUT_ATTEMPT_DEADLINE,
-        LockPatternUtils.PATTERN_EVER_CHOSEN_KEY,
-        LockPatternUtils.PASSWORD_TYPE_KEY,
-        LockPatternUtils.PASSWORD_TYPE_ALTERNATE_KEY,
-        LockPatternUtils.LOCK_PASSWORD_SALT_KEY,
-        LockPatternUtils.DISABLE_LOCKSCREEN_KEY,
-        LockPatternUtils.LOCKSCREEN_OPTIONS,
-        LockPatternUtils.LOCKSCREEN_BIOMETRIC_WEAK_FALLBACK,
-        LockPatternUtils.BIOMETRIC_WEAK_EVER_CHOSEN_KEY,
-        LockPatternUtils.LOCKSCREEN_POWER_BUTTON_INSTANTLY_LOCKS,
-        LockPatternUtils.PASSWORD_HISTORY_KEY,
-        Secure.LOCK_PATTERN_ENABLED,
-        Secure.LOCK_BIOMETRIC_WEAK_FLAGS,
-        Secure.LOCK_PATTERN_VISIBLE,
-        Secure.LOCK_PATTERN_TACTILE_FEEDBACK_ENABLED
+            LockPatternUtils.LOCKOUT_PERMANENT_KEY,
+            LockPatternUtils.LOCKOUT_ATTEMPT_DEADLINE,
+            LockPatternUtils.PATTERN_EVER_CHOSEN_KEY,
+            LockPatternUtils.PASSWORD_TYPE_KEY,
+            LockPatternUtils.PASSWORD_TYPE_ALTERNATE_KEY,
+            LockPatternUtils.LOCK_PASSWORD_SALT_KEY,
+            LockPatternUtils.DISABLE_LOCKSCREEN_KEY,
+            LockPatternUtils.LOCKSCREEN_OPTIONS,
+            LockPatternUtils.LOCKSCREEN_BIOMETRIC_WEAK_FALLBACK,
+            LockPatternUtils.BIOMETRIC_WEAK_EVER_CHOSEN_KEY,
+            LockPatternUtils.LOCKSCREEN_POWER_BUTTON_INSTANTLY_LOCKS,
+            LockPatternUtils.PASSWORD_HISTORY_KEY,
+            Secure.LOCK_PATTERN_ENABLED,
+            Secure.LOCK_BIOMETRIC_WEAK_FLAGS,
+            Secure.LOCK_PATTERN_VISIBLE,
+            Secure.LOCK_PATTERN_TACTILE_FEEDBACK_ENABLED
     };
 
     // Reading these settings needs the contacts permission
     private static final String[] READ_CONTACTS_PROTECTED_SETTINGS = new String[] {
-        Secure.LOCK_SCREEN_OWNER_INFO_ENABLED,
-        Secure.LOCK_SCREEN_OWNER_INFO
+            Secure.LOCK_SCREEN_OWNER_INFO_ENABLED,
+            Secure.LOCK_SCREEN_OWNER_INFO
     };
 
     // Reading these settings needs the same permission as checking the password
@@ -1694,8 +1555,8 @@
     };
 
     private static final String[] SETTINGS_TO_BACKUP = new String[] {
-        Secure.LOCK_SCREEN_OWNER_INFO_ENABLED,
-        Secure.LOCK_SCREEN_OWNER_INFO
+            Secure.LOCK_SCREEN_OWNER_INFO_ENABLED,
+            Secure.LOCK_SCREEN_OWNER_INFO
     };
 
     private IStorageManager getStorageManager() {
@@ -1720,8 +1581,7 @@
             return mGateKeeperService;
         }
 
-        final IBinder service =
-            ServiceManager.getService(Context.GATEKEEPER_SERVICE);
+        final IBinder service = ServiceManager.getService(Context.GATEKEEPER_SERVICE);
         if (service != null) {
             service.linkToDeath(new GateKeeperDiedRecipient(), 0);
             mGateKeeperService = IGateKeeperService.Stub.asInterface(service);
diff --git a/services/core/java/com/android/server/LockSettingsStorage.java b/services/core/java/com/android/server/LockSettingsStorage.java
index ab91a73..3d973a0 100644
--- a/services/core/java/com/android/server/LockSettingsStorage.java
+++ b/services/core/java/com/android/server/LockSettingsStorage.java
@@ -16,7 +16,7 @@
 
 package com.android.server;
 
-import com.android.internal.annotations.VisibleForTesting;
+import static android.content.Context.USER_SERVICE;
 
 import android.content.ContentValues;
 import android.content.Context;
@@ -29,14 +29,15 @@
 import android.util.ArrayMap;
 import android.util.Log;
 import android.util.Slog;
-import android.util.SparseArray;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.ArrayUtils;
+import com.android.internal.widget.LockPatternUtils;
 
 import java.io.File;
 import java.io.IOException;
 import java.io.RandomAccessFile;
 
-import static android.content.Context.USER_SERVICE;
-
 /**
  * Storage for the lock settings service.
  */
@@ -72,29 +73,48 @@
     private final Cache mCache = new Cache();
     private final Object mFileWriteLock = new Object();
 
-    private SparseArray<Integer> mStoredCredentialType;
-
-    static class CredentialHash {
-        static final int TYPE_NONE = -1;
-        static final int TYPE_PATTERN = 1;
-        static final int TYPE_PASSWORD = 2;
-
+    @VisibleForTesting
+    public static class CredentialHash {
         static final int VERSION_LEGACY = 0;
         static final int VERSION_GATEKEEPER = 1;
 
-        CredentialHash(byte[] hash, int version) {
+        private CredentialHash(byte[] hash, int type, int version) {
+            if (type != LockPatternUtils.CREDENTIAL_TYPE_NONE) {
+                if (hash == null) {
+                    throw new RuntimeException("Empty hash for CredentialHash");
+                }
+            } else /* type == LockPatternUtils.CREDENTIAL_TYPE_NONE */ {
+                if (hash != null) {
+                    throw new RuntimeException("None type CredentialHash should not have hash");
+                }
+            }
             this.hash = hash;
+            this.type = type;
             this.version = version;
             this.isBaseZeroPattern = false;
         }
 
-        CredentialHash(byte[] hash, boolean isBaseZeroPattern) {
+        private CredentialHash(byte[] hash, boolean isBaseZeroPattern) {
             this.hash = hash;
+            this.type = LockPatternUtils.CREDENTIAL_TYPE_PATTERN;
             this.version = VERSION_GATEKEEPER;
             this.isBaseZeroPattern = isBaseZeroPattern;
         }
 
+        static CredentialHash create(byte[] hash, int type) {
+            if (type == LockPatternUtils.CREDENTIAL_TYPE_NONE) {
+                throw new RuntimeException("Bad type for CredentialHash");
+            }
+            return new CredentialHash(hash, type, VERSION_GATEKEEPER);
+        }
+
+        static CredentialHash createEmptyHash() {
+            return new CredentialHash(null, LockPatternUtils.CREDENTIAL_TYPE_NONE,
+                    VERSION_GATEKEEPER);
+        }
+
         byte[] hash;
+        int type;
         int version;
         boolean isBaseZeroPattern;
     }
@@ -102,7 +122,6 @@
     public LockSettingsStorage(Context context, Callback callback) {
         mContext = context;
         mOpenHelper = new DatabaseHelper(context, callback);
-        mStoredCredentialType = new SparseArray<Integer>();
     }
 
     public void writeKeyValue(String key, String value, int userId) {
@@ -178,75 +197,62 @@
         }
 
         // Populate cache by reading the password and pattern files.
-        readPasswordHash(userId);
-        readPatternHash(userId);
+        readCredentialHash(userId);
     }
 
-    public int getStoredCredentialType(int userId) {
-        final Integer cachedStoredCredentialType = mStoredCredentialType.get(userId);
-        if (cachedStoredCredentialType != null) {
-            return cachedStoredCredentialType.intValue();
-        }
-
-        int storedCredentialType;
-        CredentialHash pattern = readPatternHash(userId);
-        if (pattern == null) {
-            if (readPasswordHash(userId) != null) {
-                storedCredentialType = CredentialHash.TYPE_PASSWORD;
-            } else {
-                storedCredentialType = CredentialHash.TYPE_NONE;
-            }
-        } else {
-            CredentialHash password = readPasswordHash(userId);
-            if (password != null) {
-                // Both will never be GateKeeper
-                if (password.version == CredentialHash.VERSION_GATEKEEPER) {
-                    storedCredentialType = CredentialHash.TYPE_PASSWORD;
-                } else {
-                    storedCredentialType = CredentialHash.TYPE_PATTERN;
-                }
-            } else {
-                storedCredentialType = CredentialHash.TYPE_PATTERN;
-            }
-        }
-        mStoredCredentialType.put(userId, storedCredentialType);
-        return storedCredentialType;
-    }
-
-
-    public CredentialHash readPasswordHash(int userId) {
+    private CredentialHash readPasswordHashIfExists(int userId) {
         byte[] stored = readFile(getLockPasswordFilename(userId));
-        if (stored != null && stored.length > 0) {
-            return new CredentialHash(stored, CredentialHash.VERSION_GATEKEEPER);
+        if (!ArrayUtils.isEmpty(stored)) {
+            return new CredentialHash(stored, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD,
+                    CredentialHash.VERSION_GATEKEEPER);
         }
 
         stored = readFile(getLegacyLockPasswordFilename(userId));
-        if (stored != null && stored.length > 0) {
-            return new CredentialHash(stored, CredentialHash.VERSION_LEGACY);
+        if (!ArrayUtils.isEmpty(stored)) {
+            return new CredentialHash(stored, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD,
+                    CredentialHash.VERSION_LEGACY);
         }
-
         return null;
     }
 
-    public CredentialHash readPatternHash(int userId) {
+    private CredentialHash readPatternHashIfExists(int userId) {
         byte[] stored = readFile(getLockPatternFilename(userId));
-        if (stored != null && stored.length > 0) {
-            return new CredentialHash(stored, CredentialHash.VERSION_GATEKEEPER);
+        if (!ArrayUtils.isEmpty(stored)) {
+            return new CredentialHash(stored, LockPatternUtils.CREDENTIAL_TYPE_PATTERN,
+                    CredentialHash.VERSION_GATEKEEPER);
         }
 
         stored = readFile(getBaseZeroLockPatternFilename(userId));
-        if (stored != null && stored.length > 0) {
+        if (!ArrayUtils.isEmpty(stored)) {
             return new CredentialHash(stored, true);
         }
 
         stored = readFile(getLegacyLockPatternFilename(userId));
-        if (stored != null && stored.length > 0) {
-            return new CredentialHash(stored, CredentialHash.VERSION_LEGACY);
+        if (!ArrayUtils.isEmpty(stored)) {
+            return new CredentialHash(stored, LockPatternUtils.CREDENTIAL_TYPE_PATTERN,
+                    CredentialHash.VERSION_LEGACY);
         }
-
         return null;
     }
 
+    public CredentialHash readCredentialHash(int userId) {
+        CredentialHash passwordHash = readPasswordHashIfExists(userId);
+        CredentialHash patternHash = readPatternHashIfExists(userId);
+        if (passwordHash != null && patternHash != null) {
+            if (passwordHash.version == CredentialHash.VERSION_GATEKEEPER) {
+                return passwordHash;
+            } else {
+                return patternHash;
+            }
+        } else if (passwordHash != null) {
+            return passwordHash;
+        } else if (patternHash != null) {
+            return patternHash;
+        } else {
+            return CredentialHash.createEmptyHash();
+        }
+    }
+
     public void removeChildProfileLock(int userId) {
         if (DEBUG)
             Slog.e(TAG, "Remove child profile lock for user: " + userId);
@@ -355,26 +361,17 @@
         }
     }
 
-    public void writePatternHash(byte[] hash, int userId) {
-        mStoredCredentialType.put(userId, hash == null ? CredentialHash.TYPE_NONE
-                : CredentialHash.TYPE_PATTERN);
-        writeFile(getLockPatternFilename(userId), hash);
-        clearPasswordHash(userId);
-    }
+    public void writeCredentialHash(CredentialHash hash, int userId) {
+        byte[] patternHash = null;
+        byte[] passwordHash = null;
 
-    private void clearPatternHash(int userId) {
-        writeFile(getLockPatternFilename(userId), null);
-    }
-
-    public void writePasswordHash(byte[] hash, int userId) {
-        mStoredCredentialType.put(userId, hash == null ? CredentialHash.TYPE_NONE
-                : CredentialHash.TYPE_PASSWORD);
-        writeFile(getLockPasswordFilename(userId), hash);
-        clearPatternHash(userId);
-    }
-
-    private void clearPasswordHash(int userId) {
-        writeFile(getLockPasswordFilename(userId), null);
+        if (hash.type == LockPatternUtils.CREDENTIAL_TYPE_PASSWORD) {
+            passwordHash = hash.hash;
+        } else if (hash.type == LockPatternUtils.CREDENTIAL_TYPE_PATTERN) {
+            patternHash = hash.hash;
+        }
+        writeFile(getLockPasswordFilename(userId), passwordHash);
+        writeFile(getLockPatternFilename(userId), patternHash);
     }
 
     @VisibleForTesting
diff --git a/services/tests/servicestests/src/com/android/server/LockSettingsStorageTests.java b/services/tests/servicestests/src/com/android/server/LockSettingsStorageTests.java
index 7d28e39..9d52153 100644
--- a/services/tests/servicestests/src/com/android/server/LockSettingsStorageTests.java
+++ b/services/tests/servicestests/src/com/android/server/LockSettingsStorageTests.java
@@ -16,6 +16,10 @@
 
 package com.android.server;
 
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
 import android.content.Context;
 import android.content.ContextWrapper;
 import android.content.pm.UserInfo;
@@ -24,13 +28,23 @@
 import android.os.UserManager;
 import android.test.AndroidTestCase;
 
+import com.android.internal.widget.LockPatternUtils;
+import com.android.server.LockSettingsStorage.CredentialHash;
 import java.io.File;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
 import java.util.concurrent.CountDownLatch;
 
+/**
+ * runtest frameworks-services -c com.android.server.LockSettingsStorageTests
+ */
 public class LockSettingsStorageTests extends AndroidTestCase {
+    private final byte[] PASSWORD_0 = "thepassword0".getBytes();
+    private final byte[] PASSWORD_1 = "password1".getBytes();
+    private final byte[] PATTERN_0 = "123654".getBytes();
+    private final byte[] PATTERN_1 = "147852369".getBytes();
+
     LockSettingsStorage mStorage;
     File mStorageDir;
 
@@ -47,24 +61,16 @@
         assertTrue(!mDb.exists() || mDb.delete());
 
         final Context ctx = getContext();
+        final UserManager mockUserManager = mock(UserManager.class);
+        // User 2 is a profile of user 1.
+        when(mockUserManager.getProfileParent(eq(2))).thenReturn(new UserInfo(1, "name", 0));
+        // User 3 is a profile of user 0.
+        when(mockUserManager.getProfileParent(eq(3))).thenReturn(new UserInfo(0, "name", 0));
         setContext(new ContextWrapper(ctx) {
             @Override
             public Object getSystemService(String name) {
                 if (USER_SERVICE.equals(name)) {
-                    return new UserManager(ctx, null) {
-                        @Override
-                        public UserInfo getProfileParent(int userHandle) {
-                            if (userHandle == 2) {
-                                // User 2 is a profile of user 1.
-                                return new UserInfo(1, "name", 0);
-                            }
-                            if (userHandle == 3) {
-                                // User 3 is a profile of user 0.
-                                return new UserInfo(0, "name", 0);
-                            }
-                            return null;
-                        }
-                    };
+                    return mockUserManager;
                 }
                 return super.getSystemService(name);
             }
@@ -201,151 +207,149 @@
 
     public void testRemoveUser() {
         mStorage.writeKeyValue("key", "value", 0);
-        mStorage.writePasswordHash(new byte[]{1}, 0);
-        mStorage.writePatternHash(new byte[]{2}, 0);
+        writePasswordBytes(PASSWORD_0, 0);
+        writePatternBytes(PATTERN_0, 0);
 
         mStorage.writeKeyValue("key", "value", 1);
-        mStorage.writePasswordHash(new byte[]{1}, 1);
-        mStorage.writePatternHash(new byte[]{2}, 1);
+        writePasswordBytes(PASSWORD_1, 1);
+        writePatternBytes(PATTERN_1, 1);
 
         mStorage.removeUser(0);
 
         assertEquals("value", mStorage.readKeyValue("key", "default", 1));
         assertEquals("default", mStorage.readKeyValue("key", "default", 0));
-        assertNotNull(mStorage.readPasswordHash(1));
-        assertNull(mStorage.readPasswordHash(0));
-        assertNotNull(mStorage.readPatternHash(1));
-        assertNull(mStorage.readPatternHash(0));
+        assertEquals(LockPatternUtils.CREDENTIAL_TYPE_NONE, mStorage.readCredentialHash(0).type);
+        assertPatternBytes(PATTERN_1, 1);
     }
 
-    public void testPassword_Default() {
-        assertNull(mStorage.readPasswordHash(0));
+    public void testCredential_Default() {
+        assertEquals(mStorage.readCredentialHash(0).type, LockPatternUtils.CREDENTIAL_TYPE_NONE);
     }
 
     public void testPassword_Write() {
-        mStorage.writePasswordHash("thepassword".getBytes(), 0);
+        writePasswordBytes(PASSWORD_0, 0);
 
-        assertArrayEquals("thepassword".getBytes(), mStorage.readPasswordHash(0).hash);
+        assertPasswordBytes(PASSWORD_0, 0);
         mStorage.clearCache();
-        assertArrayEquals("thepassword".getBytes(), mStorage.readPasswordHash(0).hash);
+        assertPasswordBytes(PASSWORD_0, 0);
     }
 
     public void testPassword_WriteProfileWritesParent() {
-        mStorage.writePasswordHash("parentpasswordd".getBytes(), 1);
-        mStorage.writePasswordHash("profilepassword".getBytes(), 2);
+        writePasswordBytes(PASSWORD_0, 1);
+        writePasswordBytes(PASSWORD_1, 2);
 
-        assertArrayEquals("profilepassword".getBytes(), mStorage.readPasswordHash(1).hash);
-        assertArrayEquals("profilepassword".getBytes(), mStorage.readPasswordHash(2).hash);
+        assertPasswordBytes(PASSWORD_0, 1);
+        assertPasswordBytes(PASSWORD_1, 2);
         mStorage.clearCache();
-        assertArrayEquals("profilepassword".getBytes(), mStorage.readPasswordHash(1).hash);
-        assertArrayEquals("profilepassword".getBytes(), mStorage.readPasswordHash(2).hash);
+        assertPasswordBytes(PASSWORD_0, 1);
+        assertPasswordBytes(PASSWORD_1, 2);
     }
 
     public void testLockType_WriteProfileWritesParent() {
-        mStorage.writePasswordHash("parentpassword".getBytes(), 10);
-        mStorage.writePatternHash("12345678".getBytes(), 20);
+        writePasswordBytes(PASSWORD_0, 10);
+        writePatternBytes(PATTERN_0, 20);
 
-        assertEquals(2, mStorage.getStoredCredentialType(10));
-        assertEquals(1, mStorage.getStoredCredentialType(20));
+        assertEquals(LockPatternUtils.CREDENTIAL_TYPE_PASSWORD,
+                mStorage.readCredentialHash(10).type);
+        assertEquals(LockPatternUtils.CREDENTIAL_TYPE_PATTERN,
+                mStorage.readCredentialHash(20).type);
         mStorage.clearCache();
-        assertEquals(2, mStorage.getStoredCredentialType(10));
-        assertEquals(1, mStorage.getStoredCredentialType(20));
+        assertEquals(LockPatternUtils.CREDENTIAL_TYPE_PASSWORD,
+                mStorage.readCredentialHash(10).type);
+        assertEquals(LockPatternUtils.CREDENTIAL_TYPE_PATTERN,
+                mStorage.readCredentialHash(20).type);
+    }
+
+    public void testPassword_WriteParentWritesProfile() {
+        writePasswordBytes(PASSWORD_0, 2);
+        writePasswordBytes(PASSWORD_1, 1);
+
+        assertPasswordBytes(PASSWORD_1, 1);
+        assertPasswordBytes(PASSWORD_0, 2);
+        mStorage.clearCache();
+        assertPasswordBytes(PASSWORD_1, 1);
+        assertPasswordBytes(PASSWORD_0, 2);
     }
 
     public void testProfileLock_ReadWriteChildProfileLock() {
         assertFalse(mStorage.hasChildProfileLock(20));
-        mStorage.writeChildProfileLock(20, "profilepassword".getBytes());
-        assertArrayEquals("profilepassword".getBytes(), mStorage.readChildProfileLock(20));
+        mStorage.writeChildProfileLock(20, PASSWORD_0);
+        assertArrayEquals(PASSWORD_0, mStorage.readChildProfileLock(20));
         assertTrue(mStorage.hasChildProfileLock(20));
         mStorage.clearCache();
-        assertArrayEquals("profilepassword".getBytes(), mStorage.readChildProfileLock(20));
+        assertArrayEquals(PASSWORD_0, mStorage.readChildProfileLock(20));
         assertTrue(mStorage.hasChildProfileLock(20));
     }
 
-    public void testPassword_WriteParentWritesProfile() {
-        mStorage.writePasswordHash("profilepassword".getBytes(), 2);
-        mStorage.writePasswordHash("parentpasswordd".getBytes(), 1);
-
-        assertArrayEquals("parentpasswordd".getBytes(), mStorage.readPasswordHash(1).hash);
-        assertArrayEquals("parentpasswordd".getBytes(), mStorage.readPasswordHash(2).hash);
-        mStorage.clearCache();
-        assertArrayEquals("parentpasswordd".getBytes(), mStorage.readPasswordHash(1).hash);
-        assertArrayEquals("parentpasswordd".getBytes(), mStorage.readPasswordHash(2).hash);
-    }
-
-    public void testPattern_Default() {
-        assertNull(mStorage.readPasswordHash(0));
-    }
-
     public void testPattern_Write() {
-        mStorage.writePatternHash("thepattern".getBytes(), 0);
+        writePatternBytes(PATTERN_0, 0);
 
-        assertArrayEquals("thepattern".getBytes(), mStorage.readPatternHash(0).hash);
+        assertPatternBytes(PATTERN_0, 0);
         mStorage.clearCache();
-        assertArrayEquals("thepattern".getBytes(), mStorage.readPatternHash(0).hash);
+        assertPatternBytes(PATTERN_0, 0);
     }
 
     public void testPattern_WriteProfileWritesParent() {
-        mStorage.writePatternHash("parentpatternn".getBytes(), 1);
-        mStorage.writePatternHash("profilepattern".getBytes(), 2);
+        writePatternBytes(PATTERN_0, 1);
+        writePatternBytes(PATTERN_1, 2);
 
-        assertArrayEquals("profilepattern".getBytes(), mStorage.readPatternHash(1).hash);
-        assertArrayEquals("profilepattern".getBytes(), mStorage.readPatternHash(2).hash);
+        assertPatternBytes(PATTERN_0, 1);
+        assertPatternBytes(PATTERN_1, 2);
         mStorage.clearCache();
-        assertArrayEquals("profilepattern".getBytes(), mStorage.readPatternHash(1).hash);
-        assertArrayEquals("profilepattern".getBytes(), mStorage.readPatternHash(2).hash);
+        assertPatternBytes(PATTERN_0, 1);
+        assertPatternBytes(PATTERN_1, 2);
     }
 
     public void testPattern_WriteParentWritesProfile() {
-        mStorage.writePatternHash("profilepattern".getBytes(), 2);
-        mStorage.writePatternHash("parentpatternn".getBytes(), 1);
+        writePatternBytes(PATTERN_1, 2);
+        writePatternBytes(PATTERN_0, 1);
 
-        assertArrayEquals("parentpatternn".getBytes(), mStorage.readPatternHash(1).hash);
-        assertArrayEquals("parentpatternn".getBytes(), mStorage.readPatternHash(2).hash);
+        assertPatternBytes(PATTERN_0, 1);
+        assertPatternBytes(PATTERN_1, 2);
         mStorage.clearCache();
-        assertArrayEquals("parentpatternn".getBytes(), mStorage.readPatternHash(1).hash);
-        assertArrayEquals("parentpatternn".getBytes(), mStorage.readPatternHash(2).hash);
+        assertPatternBytes(PATTERN_0, 1);
+        assertPatternBytes(PATTERN_1, 2);
     }
 
     public void testPrefetch() {
         mStorage.writeKeyValue("key", "toBeFetched", 0);
-        mStorage.writePatternHash("pattern".getBytes(), 0);
-        mStorage.writePasswordHash("password".getBytes(), 0);
+        writePatternBytes(PATTERN_0, 0);
 
         mStorage.clearCache();
         mStorage.prefetchUser(0);
 
         assertEquals("toBeFetched", mStorage.readKeyValue("key", "default", 0));
-        assertArrayEquals("pattern".getBytes(), mStorage.readPatternHash(0).hash);
-        assertArrayEquals("password".getBytes(), mStorage.readPasswordHash(0).hash);
+        assertPatternBytes(PATTERN_0, 0);
     }
 
     public void testFileLocation_Owner() {
         LockSettingsStorage storage = new LockSettingsStorage(getContext(), null);
 
-        assertEquals("/data/system/gesture.key", storage.getLockPatternFilename(0));
-        assertEquals("/data/system/password.key", storage.getLockPasswordFilename(0));
+        assertEquals("/data/system/gesture.key", storage.getLegacyLockPatternFilename(0));
+        assertEquals("/data/system/password.key", storage.getLegacyLockPasswordFilename(0));
+        assertEquals("/data/system/gatekeeper.pattern.key", storage.getLockPatternFilename(0));
+        assertEquals("/data/system/gatekeeper.password.key", storage.getLockPasswordFilename(0));
     }
 
     public void testFileLocation_SecondaryUser() {
         LockSettingsStorage storage = new LockSettingsStorage(getContext(), null);
 
-        assertEquals("/data/system/users/1/gesture.key", storage.getLockPatternFilename(1));
-        assertEquals("/data/system/users/1/password.key", storage.getLockPasswordFilename(1));
+        assertEquals("/data/system/users/1/gatekeeper.pattern.key", storage.getLockPatternFilename(1));
+        assertEquals("/data/system/users/1/gatekeeper.password.key", storage.getLockPasswordFilename(1));
     }
 
     public void testFileLocation_ProfileToSecondary() {
         LockSettingsStorage storage = new LockSettingsStorage(getContext(), null);
 
-        assertEquals("/data/system/users/1/gesture.key", storage.getLockPatternFilename(2));
-        assertEquals("/data/system/users/1/password.key", storage.getLockPasswordFilename(2));
+        assertEquals("/data/system/users/2/gatekeeper.pattern.key", storage.getLockPatternFilename(2));
+        assertEquals("/data/system/users/2/gatekeeper.password.key", storage.getLockPasswordFilename(2));
     }
 
     public void testFileLocation_ProfileToOwner() {
         LockSettingsStorage storage = new LockSettingsStorage(getContext(), null);
 
-        assertEquals("/data/system/gesture.key", storage.getLockPatternFilename(3));
-        assertEquals("/data/system/password.key", storage.getLockPasswordFilename(3));
+        assertEquals("/data/system/users/3/gatekeeper.pattern.key", storage.getLockPatternFilename(3));
+        assertEquals("/data/system/users/3/gatekeeper.password.key", storage.getLockPasswordFilename(3));
     }
 
     private static void assertArrayEquals(byte[] expected, byte[] actual) {
@@ -354,4 +358,26 @@
                     "> but was:<" + Arrays.toString(actual) + ">");
         }
     }
+
+    private void writePasswordBytes(byte[] password, int userId) {
+        mStorage.writeCredentialHash(CredentialHash.create(
+                password, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD), userId);
+    }
+
+    private void writePatternBytes(byte[] pattern, int userId) {
+        mStorage.writeCredentialHash(CredentialHash.create(
+                pattern, LockPatternUtils.CREDENTIAL_TYPE_PATTERN), userId);
+    }
+
+    private void assertPasswordBytes(byte[] password, int userId) {
+        CredentialHash cred = mStorage.readCredentialHash(userId);
+        assertEquals(LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, cred.type);
+        assertArrayEquals(password, cred.hash);
+    }
+
+    private void assertPatternBytes(byte[] pattern, int userId) {
+        CredentialHash cred = mStorage.readCredentialHash(userId);
+        assertEquals(LockPatternUtils.CREDENTIAL_TYPE_PATTERN, cred.type);
+        assertArrayEquals(pattern, cred.hash);
+    }
 }