Be more forgiving in parsing verifier IDs

RFC 4648's Base32 is made to be forgiving for users doing manual entry
of the digits. For this reason we want to be able to parse lowercase
letters as uppercase and number 0 (zero) as letter O along with number
1 (one) as letter I.

Cherry-pick into ics-factoryrom

Change-Id: I149d8e947ff8b36305c1e61d8a1d80ff0b317cda
diff --git a/core/java/android/content/pm/VerifierDeviceIdentity.java b/core/java/android/content/pm/VerifierDeviceIdentity.java
index bfebe0f..a8cdb6a 100644
--- a/core/java/android/content/pm/VerifierDeviceIdentity.java
+++ b/core/java/android/content/pm/VerifierDeviceIdentity.java
@@ -153,6 +153,15 @@
                 value = group - ('2' - 26);
             } else if (group == SEPARATOR) {
                 continue;
+            } else if ('a' <= group && group <= 'z') {
+                /* Lowercase letters should be the same as uppercase for Base32 */
+                value = group - 'a';
+            } else if (group == '0') {
+                /* Be nice to users that mistake O (letter) for 0 (zero) */
+                value = 'O' - 'A';
+            } else if (group == '1') {
+                /* Be nice to users that mistake I (letter) for 1 (one) */
+                value = 'I' - 'A';
             } else {
                 throw new IllegalArgumentException("base base-32 character: " + group);
             }
diff --git a/core/tests/coretests/src/android/content/pm/VerifierDeviceIdentityTest.java b/core/tests/coretests/src/android/content/pm/VerifierDeviceIdentityTest.java
index e6a6a26..cb13eb7 100644
--- a/core/tests/coretests/src/android/content/pm/VerifierDeviceIdentityTest.java
+++ b/core/tests/coretests/src/android/content/pm/VerifierDeviceIdentityTest.java
@@ -25,6 +25,8 @@
 
     private static final String TEST_1_ENCODED = "HUXY-A75N-FLKV-F";
 
+    private static final String TEST_1_ENCODED_LOWERCASE = "huxy-a75n-flkv-f";
+
     private static final long TEST_2 = 0x5A05FF5A05F0A555L;
 
     private static final long TEST_MAXVALUE = Long.MAX_VALUE;
@@ -45,6 +47,10 @@
 
     private static final String TEST_OVERFLOW_ENCODED = "QAAA-AAAA-AAAA-A";
 
+    private static final String TEST_SUBSTITUTION_CORRECTED = "OIIO-IIOO-IOOI-I";
+
+    private static final String TEST_SUBSTITUTION_UNCORRECTED = "0110-1100-1001-1";
+
     public void testVerifierDeviceIdentity_Equals_Success() {
         VerifierDeviceIdentity id1 = new VerifierDeviceIdentity(TEST_1);
         VerifierDeviceIdentity id2 = new VerifierDeviceIdentity(TEST_1);
@@ -125,6 +131,7 @@
         assertEquals("Original identity and parceled identity should be the same", id1, id2);
     }
 
+    @SuppressWarnings("serial")
     private static class MockRandom extends Random {
         private long mNextLong;
 
@@ -181,7 +188,7 @@
 
     public void testVerifierDeviceIdentity_Parse_TooShort() {
         try {
-            VerifierDeviceIdentity id = VerifierDeviceIdentity.parse("AAAA-AAAA-AAAA-");
+            VerifierDeviceIdentity.parse("AAAA-AAAA-AAAA-");
             fail("Parsing should fail when device identifier is too short");
         } catch (IllegalArgumentException e) {
             // success
@@ -190,7 +197,7 @@
 
     public void testVerifierDeviceIdentity_Parse_WayTooShort() {
         try {
-            VerifierDeviceIdentity id = VerifierDeviceIdentity.parse("----------------");
+            VerifierDeviceIdentity.parse("----------------");
             fail("Parsing should fail when device identifier is too short");
         } catch (IllegalArgumentException e) {
             // success
@@ -199,7 +206,7 @@
 
     public void testVerifierDeviceIdentity_Parse_TooLong() {
         try {
-            VerifierDeviceIdentity id = VerifierDeviceIdentity.parse("AAAA-AAAA-AAAA-AA");
+            VerifierDeviceIdentity.parse("AAAA-AAAA-AAAA-AA");
             fail("Parsing should fail when device identifier is too long");
         } catch (IllegalArgumentException e) {
             // success
@@ -208,10 +215,32 @@
 
     public void testVerifierDeviceIdentity_Parse_Overflow() {
         try {
-            VerifierDeviceIdentity id = VerifierDeviceIdentity.parse(TEST_OVERFLOW_ENCODED);
+            VerifierDeviceIdentity.parse(TEST_OVERFLOW_ENCODED);
             fail("Parsing should fail when the value will overflow");
         } catch (IllegalArgumentException e) {
             // success
         }
     }
+
+    public void testVerifierDeviceIdentity_Parse_SquashToUppercase() {
+        VerifierDeviceIdentity id1 = new VerifierDeviceIdentity(TEST_1);
+
+        VerifierDeviceIdentity id2 = VerifierDeviceIdentity.parse(TEST_1_ENCODED_LOWERCASE);
+
+        assertEquals("Lowercase should parse to be the same as uppercase", id1, id2);
+
+        assertEquals("Substituted identity should render to the same string",
+                id1.toString(), id2.toString());
+    }
+
+    public void testVerifierDeviceIdentity_Parse_1I_And_0O_Substitution() {
+        VerifierDeviceIdentity id1 = VerifierDeviceIdentity.parse(TEST_SUBSTITUTION_CORRECTED);
+
+        VerifierDeviceIdentity id2 = VerifierDeviceIdentity.parse(TEST_SUBSTITUTION_UNCORRECTED);
+
+        assertEquals("Substitution should replace 0 with O and 1 with I", id1, id2);
+
+        assertEquals("Substituted identity should render to the same string",
+                id1.toString(), id2.toString());
+    }
 }