diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeySpecParser.java b/java/src/com/android/inputmethod/keyboard/internal/KeySpecParser.java
index 2925a4b76a69daec3e2ea1d9fd253af667ca5682..8ce820cf3186be5326fcf8a8e1566cca51e30888 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/KeySpecParser.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/KeySpecParser.java
@@ -57,8 +57,15 @@ public final class KeySpecParser {
     }
 
     private static boolean hasCode(final String keySpec, final int labelEnd) {
-        if (labelEnd > 0 && labelEnd + 1 < keySpec.length()
-                && keySpec.startsWith(KeyboardCodesSet.PREFIX_CODE, labelEnd + 1)) {
+        if (labelEnd <= 0 || labelEnd + 1 >= keySpec.length()) {
+            return false;
+        }
+        if (keySpec.startsWith(KeyboardCodesSet.PREFIX_CODE, labelEnd + 1)) {
+            return true;
+        }
+        // This is a workaround to have a key that has a supplementary code point. We can't put a
+        // string in resource as a XML entity of a supplementary code point or a surrogate pair.
+        if (keySpec.startsWith(PREFIX_HEX, labelEnd + 1)) {
             return true;
         }
         return false;
@@ -203,19 +210,20 @@ public final class KeySpecParser {
         return (StringUtils.codePointCount(label) == 1) ? label.codePointAt(0) : CODE_OUTPUT_TEXT;
     }
 
-    // TODO: Make this method private once Key.code attribute is removed.
     public static int parseCode(final String text, final KeyboardCodesSet codesSet,
-            final int defCode) {
+            final int defaultCode) {
         if (text == null) {
-            return defCode;
+            return defaultCode;
         }
         if (text.startsWith(KeyboardCodesSet.PREFIX_CODE)) {
             return codesSet.getCode(text.substring(KeyboardCodesSet.PREFIX_CODE.length()));
         }
+        // This is a workaround to have a key that has a supplementary code point. We can't put a
+        // string in resource as a XML entity of a supplementary code point or a surrogate pair.
         if (text.startsWith(PREFIX_HEX)) {
             return Integer.parseInt(text.substring(PREFIX_HEX.length()), 16);
         }
-        return Integer.parseInt(text);
+        return defaultCode;
     }
 
     public static int getIconId(final String keySpec) {
diff --git a/tests/src/com/android/inputmethod/keyboard/internal/KeySpecParserTestsBase.java b/tests/src/com/android/inputmethod/keyboard/internal/KeySpecParserTestsBase.java
index f0ab7f599c4ebcbe308c5433d39ab42f5eca6bea..edeb6a6c2b4bb364fa02a53f43817cee831803f2 100644
--- a/tests/src/com/android/inputmethod/keyboard/internal/KeySpecParserTestsBase.java
+++ b/tests/src/com/android/inputmethod/keyboard/internal/KeySpecParserTestsBase.java
@@ -218,6 +218,32 @@ abstract class KeySpecParserTestsBase extends AndroidTestCase {
                 "a|c", null, ICON_UNDEFINED, mCodeSettings);
     }
 
+    public void testCodes() {
+        assertParser("Hexadecimal code", "a|0x1000",
+                "a", null, ICON_UNDEFINED, 0x1000);
+        assertParserError("Illegal hexadecimal code", "a|0x100X",
+                "a", null, ICON_UNDEFINED, CODE_UNSPECIFIED);
+        assertParser("Escaped hexadecimal code 1", "a|\\0x1000",
+                "a", "0x1000", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
+        assertParser("Escaped hexadecimal code 2", "a|0\\x1000",
+                "a", "0x1000", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
+        assertParser("Escaped hexadecimal code 2", "a|0\\x1000",
+                "a", "0x1000", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
+        assertParserError("Illegally escaped hexadecimal code", "a|0x1\\000",
+                "a", null, ICON_UNDEFINED, CODE_UNSPECIFIED);
+        // This is a workaround to have a key that has a supplementary code point. We can't put a
+        // string in resource as a XML entity of a supplementary code point or a surrogate pair.
+        // TODO: Should pass this test.
+//        assertParser("Hexadecimal supplementary code", String.format("a|0x%06x", SURROGATE_CODE2),
+//                SURROGATE_PAIR2, null, ICON_UNDEFINED, SURROGATE_CODE2);
+        assertParser("Zero is treated as output text", "a|0",
+                "a", null, ICON_UNDEFINED, '0');
+        assertParser("Digit is treated as output text", "a|3",
+                "a", null, ICON_UNDEFINED, '3');
+        assertParser("Decimal number is treated as an output text", "a|2014",
+                "a", "2014", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
+    }
+
     public void testIcons() {
         assertParser("Icon with single letter", ICON_SETTINGS + "|a",
                 null, null, mSettingsIconId, 'a');