[LockSettings] migrate patterns to be indexed at '1'

Base zero patterns (ones where the top left is idx 0)
are not handled properly by scrypt. Add logic to re-enroll
base zero patterns such that the top left is idx 1.

Bug: 21433955
Change-Id: I7f67f2c67d40dd1be6c62117710dc3b0392275a2
diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java
index aee0ff6..86d11be 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -841,7 +841,7 @@
 
         final byte[] bytes = string.getBytes();
         for (int i = 0; i < bytes.length; i++) {
-            byte b = bytes[i];
+            byte b = (byte) (bytes[i] - '1');
             result.add(LockPatternView.Cell.of(b / 3, b % 3));
         }
         return result;
@@ -861,7 +861,21 @@
         byte[] res = new byte[patternSize];
         for (int i = 0; i < patternSize; i++) {
             LockPatternView.Cell cell = pattern.get(i);
-            res[i] = (byte) (cell.getRow() * 3 + cell.getColumn());
+            res[i] = (byte) (cell.getRow() * 3 + cell.getColumn() + '1');
+        }
+        return new String(res);
+    }
+
+    public static String patternStringToBaseZero(String pattern) {
+        if (pattern == null) {
+            return "";
+        }
+        final int patternSize = pattern.length();
+
+        byte[] res = new byte[patternSize];
+        final byte[] bytes = pattern.getBytes();
+        for (int i = 0; i < patternSize; i++) {
+            res[i] = (byte) (bytes[i] - '1');
         }
         return new String(res);
     }
diff --git a/services/core/java/com/android/server/LockSettingsService.java b/services/core/java/com/android/server/LockSettingsService.java
index f6ca0d7..5436ce0 100644
--- a/services/core/java/com/android/server/LockSettingsService.java
+++ b/services/core/java/com/android/server/LockSettingsService.java
@@ -502,12 +502,21 @@
         return doVerifyPattern(pattern, true, challenge, userId);
     }
 
-    private VerifyCredentialResponse doVerifyPattern(String pattern, boolean hasChallenge, long challenge,
-            int userId) throws RemoteException {
+    private VerifyCredentialResponse doVerifyPattern(String pattern, boolean hasChallenge,
+            long challenge, int userId) throws RemoteException {
        checkPasswordReadPermission(userId);
        CredentialHash storedHash = mStorage.readPatternHash(userId);
-       return verifyCredential(userId, storedHash, pattern, hasChallenge,
-               challenge,
+       boolean shouldReEnrollBaseZero = storedHash != null && storedHash.isBaseZeroPattern;
+
+       String patternToVerify;
+       if (shouldReEnrollBaseZero) {
+           patternToVerify = LockPatternUtils.patternStringToBaseZero(pattern);
+       } else {
+           patternToVerify = pattern;
+       }
+
+       VerifyCredentialResponse response = verifyCredential(userId, storedHash, patternToVerify,
+               hasChallenge, challenge,
                new CredentialUtil() {
                    @Override
                    public void setCredential(String pattern, String oldPattern, int userId)
@@ -517,11 +526,19 @@
 
                    @Override
                    public byte[] toHash(String pattern, int userId) {
-                       return mLockPatternUtils.patternToHash(
-                               mLockPatternUtils.stringToPattern(pattern));
+                       return LockPatternUtils.patternToHash(
+                               LockPatternUtils.stringToPattern(pattern));
                    }
                }
        );
+
+       if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_OK
+               && shouldReEnrollBaseZero) {
+           setLockPattern(pattern, patternToVerify, userId);
+       }
+
+       return response;
+
     }
 
     @Override
diff --git a/services/core/java/com/android/server/LockSettingsStorage.java b/services/core/java/com/android/server/LockSettingsStorage.java
index f202c36..de48e71 100644
--- a/services/core/java/com/android/server/LockSettingsStorage.java
+++ b/services/core/java/com/android/server/LockSettingsStorage.java
@@ -56,7 +56,8 @@
     };
 
     private static final String SYSTEM_DIRECTORY = "/system/";
-    private static final String LOCK_PATTERN_FILE = "gatekeeper.gesture.key";
+    private static final String LOCK_PATTERN_FILE = "gatekeeper.pattern.key";
+    private static final String BASE_ZERO_LOCK_PATTERN_FILE = "gatekeeper.gesture.key";
     private static final String LEGACY_LOCK_PATTERN_FILE = "gesture.key";
     private static final String LOCK_PASSWORD_FILE = "gatekeeper.password.key";
     private static final String LEGACY_LOCK_PASSWORD_FILE = "password.key";
@@ -81,10 +82,18 @@
         CredentialHash(byte[] hash, int version) {
             this.hash = hash;
             this.version = version;
+            this.isBaseZeroPattern = false;
+        }
+
+        CredentialHash(byte[] hash, boolean isBaseZeroPattern) {
+            this.hash = hash;
+            this.version = VERSION_GATEKEEPER;
+            this.isBaseZeroPattern = isBaseZeroPattern;
         }
 
         byte[] hash;
         int version;
+        boolean isBaseZeroPattern;
     }
 
     public LockSettingsStorage(Context context, Callback callback) {
@@ -219,6 +228,11 @@
             return new CredentialHash(stored, CredentialHash.VERSION_GATEKEEPER);
         }
 
+        stored = readFile(getBaseZeroLockPatternFilename(userId));
+        if (stored != null && stored.length > 0) {
+            return new CredentialHash(stored, true);
+        }
+
         stored = readFile(getLegacyLockPatternFilename(userId));
         if (stored != null && stored.length > 0) {
             return new CredentialHash(stored, CredentialHash.VERSION_LEGACY);
@@ -227,6 +241,7 @@
         return null;
     }
 
+
     public boolean hasPassword(int userId) {
         return hasFile(getLockPasswordFilename(userId)) ||
             hasFile(getLegacyLockPasswordFilename(userId));
@@ -234,6 +249,7 @@
 
     public boolean hasPattern(int userId) {
         return hasFile(getLockPatternFilename(userId)) ||
+            hasFile(getBaseZeroLockPatternFilename(userId)) ||
             hasFile(getLegacyLockPatternFilename(userId));
     }
 
@@ -301,6 +317,13 @@
         }
     }
 
+    private void deleteFile(String name) {
+        File f = new File(name);
+        if (f != null) {
+            f.delete();
+        }
+    }
+
     public void writePatternHash(byte[] hash, int userId) {
         mStoredCredentialType = hash == null
             ? CredentialHash.TYPE_NONE
@@ -345,6 +368,10 @@
         return getLockCredentialFilePathForUser(userId, LEGACY_LOCK_PASSWORD_FILE);
     }
 
+    private String getBaseZeroLockPatternFilename(int userId) {
+        return getLockCredentialFilePathForUser(userId, BASE_ZERO_LOCK_PATTERN_FILE);
+    }
+
     private String getLockCredentialFilePathForUser(int userId, String basename) {
         userId = getUserParentOrSelfId(userId);
         String dataSystemDirectory =