Refine TypedXmlPullParser API interface.

Techincally our additions should match the shape of the upstream
XmlPullParser interface, which throws XmlPullParserException when
there is a parsing issue; IOException should be reserved for actual
disk I/O issues.

Improve BinaryXmlPullParser handling of invalid inputs to match
how ForcedTypedXmlPullParser throws on any parsing trouble.

Bug: 171832118
Test: atest FrameworksCoreTests:android.util.XmlTest
Test: atest FrameworksCoreTests:android.util.BinaryXmlTest
Change-Id: I8f0e95d536b53570055a9e174f0849cf635da48c
diff --git a/core/java/android/util/TypedXmlPullParser.java b/core/java/android/util/TypedXmlPullParser.java
index 5ff7e5d..b3b51b5 100644
--- a/core/java/android/util/TypedXmlPullParser.java
+++ b/core/java/android/util/TypedXmlPullParser.java
@@ -20,8 +20,7 @@
 import android.annotation.Nullable;
 
 import org.xmlpull.v1.XmlPullParser;
-
-import java.io.IOException;
+import org.xmlpull.v1.XmlPullParserException;
 
 /**
  * Specialization of {@link XmlPullParser} which adds explicit methods to
@@ -31,67 +30,93 @@
  */
 public interface TypedXmlPullParser extends XmlPullParser {
     /**
-     * @return decoded strongly-typed {@link #getAttributeValue}, or
-     *         {@code null} if malformed or undefined
+     * @return decoded strongly-typed {@link #getAttributeValue}
+     * @throws XmlPullParserException if the value is malformed or undefined
      */
-    @Nullable byte[] getAttributeBytesHex(@Nullable String namespace, @NonNull String name)
-            throws IOException;
-
-    /**
-     * @return decoded strongly-typed {@link #getAttributeValue}, or
-     *         {@code null} if malformed or undefined
-     */
-    @Nullable byte[] getAttributeBytesBase64(@Nullable String namespace, @NonNull String name)
-            throws IOException;
+    @NonNull byte[] getAttributeBytesHex(@Nullable String namespace, @NonNull String name)
+            throws XmlPullParserException;
 
     /**
      * @return decoded strongly-typed {@link #getAttributeValue}
-     * @throws IOException if the value is malformed or undefined
+     * @throws XmlPullParserException if the value is malformed or undefined
+     */
+    @NonNull byte[] getAttributeBytesBase64(@Nullable String namespace, @NonNull String name)
+            throws XmlPullParserException;
+
+    /**
+     * @return decoded strongly-typed {@link #getAttributeValue}
+     * @throws XmlPullParserException if the value is malformed or undefined
      */
     int getAttributeInt(@Nullable String namespace, @NonNull String name)
-            throws IOException;
+            throws XmlPullParserException;
 
     /**
      * @return decoded strongly-typed {@link #getAttributeValue}
-     * @throws IOException if the value is malformed or undefined
+     * @throws XmlPullParserException if the value is malformed or undefined
      */
     int getAttributeIntHex(@Nullable String namespace, @NonNull String name)
-            throws IOException;
+            throws XmlPullParserException;
 
     /**
      * @return decoded strongly-typed {@link #getAttributeValue}
-     * @throws IOException if the value is malformed or undefined
+     * @throws XmlPullParserException if the value is malformed or undefined
      */
     long getAttributeLong(@Nullable String namespace, @NonNull String name)
-            throws IOException;
+            throws XmlPullParserException;
 
     /**
      * @return decoded strongly-typed {@link #getAttributeValue}
-     * @throws IOException if the value is malformed or undefined
+     * @throws XmlPullParserException if the value is malformed or undefined
      */
     long getAttributeLongHex(@Nullable String namespace, @NonNull String name)
-            throws IOException;
+            throws XmlPullParserException;
 
     /**
      * @return decoded strongly-typed {@link #getAttributeValue}
-     * @throws IOException if the value is malformed or undefined
+     * @throws XmlPullParserException if the value is malformed or undefined
      */
     float getAttributeFloat(@Nullable String namespace, @NonNull String name)
-            throws IOException;
+            throws XmlPullParserException;
 
     /**
      * @return decoded strongly-typed {@link #getAttributeValue}
-     * @throws IOException if the value is malformed or undefined
+     * @throws XmlPullParserException if the value is malformed or undefined
      */
     double getAttributeDouble(@Nullable String namespace, @NonNull String name)
-            throws IOException;
+            throws XmlPullParserException;
 
     /**
      * @return decoded strongly-typed {@link #getAttributeValue}
-     * @throws IOException if the value is malformed or undefined
+     * @throws XmlPullParserException if the value is malformed or undefined
      */
     boolean getAttributeBoolean(@Nullable String namespace, @NonNull String name)
-            throws IOException;
+            throws XmlPullParserException;
+
+    /**
+     * @return decoded strongly-typed {@link #getAttributeValue}, otherwise
+     *         default value if the value is malformed or undefined
+     */
+    default @Nullable byte[] getAttributeBytesHex(@Nullable String namespace,
+            @NonNull String name, @Nullable byte[] defaultValue) {
+        try {
+            return getAttributeBytesHex(namespace, name);
+        } catch (Exception ignored) {
+            return defaultValue;
+        }
+    }
+
+    /**
+     * @return decoded strongly-typed {@link #getAttributeValue}, otherwise
+     *         default value if the value is malformed or undefined
+     */
+    default @Nullable byte[] getAttributeBytesBase64(@Nullable String namespace,
+            @NonNull String name, @Nullable byte[] defaultValue) {
+        try {
+            return getAttributeBytesBase64(namespace, name);
+        } catch (Exception ignored) {
+            return defaultValue;
+        }
+    }
 
     /**
      * @return decoded strongly-typed {@link #getAttributeValue}, otherwise
diff --git a/core/java/android/util/TypedXmlSerializer.java b/core/java/android/util/TypedXmlSerializer.java
index fe5e3e6..3f9eaa8 100644
--- a/core/java/android/util/TypedXmlSerializer.java
+++ b/core/java/android/util/TypedXmlSerializer.java
@@ -36,21 +36,21 @@
      * canonicalized, similar to {@link String#intern()}.
      */
     @NonNull XmlSerializer attributeInterned(@Nullable String namespace, @NonNull String name,
-            @Nullable String value) throws IOException;
+            @NonNull String value) throws IOException;
 
     /**
      * Encode the given strongly-typed value and serialize using
      * {@link #attribute(String, String, String)}.
      */
     @NonNull XmlSerializer attributeBytesHex(@Nullable String namespace, @NonNull String name,
-            byte[] value) throws IOException;
+            @NonNull byte[] value) throws IOException;
 
     /**
      * Encode the given strongly-typed value and serialize using
      * {@link #attribute(String, String, String)}.
      */
     @NonNull XmlSerializer attributeBytesBase64(@Nullable String namespace, @NonNull String name,
-            byte[] value) throws IOException;
+            @NonNull byte[] value) throws IOException;
 
     /**
      * Encode the given strongly-typed value and serialize using
diff --git a/core/java/com/android/internal/util/BinaryXmlPullParser.java b/core/java/com/android/internal/util/BinaryXmlPullParser.java
index da16eca..ff7743f 100644
--- a/core/java/com/android/internal/util/BinaryXmlPullParser.java
+++ b/core/java/com/android/internal/util/BinaryXmlPullParser.java
@@ -418,13 +418,14 @@
      * Search through the pool of currently allocated {@link Attribute}
      * instances for one that matches the given name.
      */
-    private @NonNull Attribute findAttribute(@NonNull String name) throws IOException {
+    private @NonNull Attribute findAttribute(@NonNull String name)
+            throws XmlPullParserException {
         for (int i = 0; i < mAttributeCount; i++) {
             if (Objects.equals(mAttributes[i].name, name)) {
                 return mAttributes[i];
             }
         }
-        throw new IOException("Missing attribute " + name);
+        throw new XmlPullParserException("Missing attribute " + name);
     }
 
     @Override
@@ -432,7 +433,7 @@
         if (namespace != null && !namespace.isEmpty()) throw illegalNamespace();
         try {
             return findAttribute(name).getValueString();
-        } catch (IOException e) {
+        } catch (XmlPullParserException e) {
             // Missing attributes default to null
             return null;
         }
@@ -444,55 +445,64 @@
     }
 
     @Override
-    public byte[] getAttributeBytesHex(String namespace, String name) throws IOException {
+    public byte[] getAttributeBytesHex(String namespace, String name)
+            throws XmlPullParserException {
         if (namespace != null && !namespace.isEmpty()) throw illegalNamespace();
         return findAttribute(name).getValueBytesHex();
     }
 
     @Override
-    public byte[] getAttributeBytesBase64(String namespace, String name) throws IOException {
+    public byte[] getAttributeBytesBase64(String namespace, String name)
+            throws XmlPullParserException {
         if (namespace != null && !namespace.isEmpty()) throw illegalNamespace();
         return findAttribute(name).getValueBytesBase64();
     }
 
     @Override
-    public int getAttributeInt(String namespace, String name) throws IOException {
+    public int getAttributeInt(String namespace, String name)
+            throws XmlPullParserException {
         if (namespace != null && !namespace.isEmpty()) throw illegalNamespace();
         return findAttribute(name).getValueInt();
     }
 
     @Override
-    public int getAttributeIntHex(String namespace, String name) throws IOException {
+    public int getAttributeIntHex(String namespace, String name)
+            throws XmlPullParserException {
         if (namespace != null && !namespace.isEmpty()) throw illegalNamespace();
         return findAttribute(name).getValueIntHex();
     }
 
     @Override
-    public long getAttributeLong(String namespace, String name) throws IOException {
+    public long getAttributeLong(String namespace, String name)
+            throws XmlPullParserException {
         if (namespace != null && !namespace.isEmpty()) throw illegalNamespace();
         return findAttribute(name).getValueLong();
     }
 
     @Override
-    public long getAttributeLongHex(String namespace, String name) throws IOException {
+    public long getAttributeLongHex(String namespace, String name)
+            throws XmlPullParserException {
         if (namespace != null && !namespace.isEmpty()) throw illegalNamespace();
         return findAttribute(name).getValueLongHex();
     }
 
     @Override
-    public float getAttributeFloat(String namespace, String name) throws IOException {
+    public float getAttributeFloat(String namespace, String name)
+            throws XmlPullParserException {
         if (namespace != null && !namespace.isEmpty()) throw illegalNamespace();
         return findAttribute(name).getValueFloat();
     }
 
     @Override
-    public double getAttributeDouble(String namespace, String name) throws IOException {
+    public double getAttributeDouble(String namespace, String name)
+            throws XmlPullParserException {
         if (namespace != null && !namespace.isEmpty()) throw illegalNamespace();
         return findAttribute(name).getValueDouble();
     }
 
     @Override
-    public boolean getAttributeBoolean(String namespace, String name) throws IOException {
+    public boolean getAttributeBoolean(String namespace, String name)
+            throws XmlPullParserException {
         if (namespace != null && !namespace.isEmpty()) throw illegalNamespace();
         return findAttribute(name).getValueBoolean();
     }
@@ -742,7 +752,7 @@
             }
         }
 
-        public @Nullable byte[] getValueBytesHex() throws IOException {
+        public @Nullable byte[] getValueBytesHex() throws XmlPullParserException {
             switch (type) {
                 case TYPE_NULL:
                     return null;
@@ -750,13 +760,18 @@
                 case TYPE_BYTES_BASE64:
                     return valueBytes;
                 case TYPE_STRING:
-                    return hexStringToBytes(valueString);
+                case TYPE_STRING_INTERNED:
+                    try {
+                        return hexStringToBytes(valueString);
+                    } catch (Exception e) {
+                        throw new XmlPullParserException("Invalid attribute " + name + ": " + e);
+                    }
                 default:
-                    throw new IOException("Invalid conversion from " + type);
+                    throw new XmlPullParserException("Invalid conversion from " + type);
             }
         }
 
-        public @Nullable byte[] getValueBytesBase64() throws IOException {
+        public @Nullable byte[] getValueBytesBase64() throws XmlPullParserException {
             switch (type) {
                 case TYPE_NULL:
                     return null;
@@ -764,98 +779,135 @@
                 case TYPE_BYTES_BASE64:
                     return valueBytes;
                 case TYPE_STRING:
-                    return Base64.decode(valueString, Base64.NO_WRAP);
+                case TYPE_STRING_INTERNED:
+                    try {
+                        return Base64.decode(valueString, Base64.NO_WRAP);
+                    } catch (Exception e) {
+                        throw new XmlPullParserException("Invalid attribute " + name + ": " + e);
+                    }
                 default:
-                    throw new IOException("Invalid conversion from " + type);
+                    throw new XmlPullParserException("Invalid conversion from " + type);
             }
         }
 
-        public int getValueInt() throws IOException {
+        public int getValueInt() throws XmlPullParserException {
             switch (type) {
                 case TYPE_INT:
                 case TYPE_INT_HEX:
                     return valueInt;
                 case TYPE_STRING:
-                    return Integer.parseInt(valueString);
+                case TYPE_STRING_INTERNED:
+                    try {
+                        return Integer.parseInt(valueString);
+                    } catch (Exception e) {
+                        throw new XmlPullParserException("Invalid attribute " + name + ": " + e);
+                    }
                 default:
-                    throw new IOException("Invalid conversion from " + type);
+                    throw new XmlPullParserException("Invalid conversion from " + type);
             }
         }
 
-        public int getValueIntHex() throws IOException {
+        public int getValueIntHex() throws XmlPullParserException {
             switch (type) {
                 case TYPE_INT:
                 case TYPE_INT_HEX:
                     return valueInt;
                 case TYPE_STRING:
-                    return Integer.parseInt(valueString, 16);
+                case TYPE_STRING_INTERNED:
+                    try {
+                        return Integer.parseInt(valueString, 16);
+                    } catch (Exception e) {
+                        throw new XmlPullParserException("Invalid attribute " + name + ": " + e);
+                    }
                 default:
-                    throw new IOException("Invalid conversion from " + type);
+                    throw new XmlPullParserException("Invalid conversion from " + type);
             }
         }
 
-        public long getValueLong() throws IOException {
+        public long getValueLong() throws XmlPullParserException {
             switch (type) {
                 case TYPE_LONG:
                 case TYPE_LONG_HEX:
                     return valueLong;
                 case TYPE_STRING:
-                    return Long.parseLong(valueString);
+                case TYPE_STRING_INTERNED:
+                    try {
+                        return Long.parseLong(valueString);
+                    } catch (Exception e) {
+                        throw new XmlPullParserException("Invalid attribute " + name + ": " + e);
+                    }
                 default:
-                    throw new IOException("Invalid conversion from " + type);
+                    throw new XmlPullParserException("Invalid conversion from " + type);
             }
         }
 
-        public long getValueLongHex() throws IOException {
+        public long getValueLongHex() throws XmlPullParserException {
             switch (type) {
                 case TYPE_LONG:
                 case TYPE_LONG_HEX:
                     return valueLong;
                 case TYPE_STRING:
-                    return Long.parseLong(valueString, 16);
+                case TYPE_STRING_INTERNED:
+                    try {
+                        return Long.parseLong(valueString, 16);
+                    } catch (Exception e) {
+                        throw new XmlPullParserException("Invalid attribute " + name + ": " + e);
+                    }
                 default:
-                    throw new IOException("Invalid conversion from " + type);
+                    throw new XmlPullParserException("Invalid conversion from " + type);
             }
         }
 
-        public float getValueFloat() throws IOException {
+        public float getValueFloat() throws XmlPullParserException {
             switch (type) {
                 case TYPE_FLOAT:
                     return valueFloat;
                 case TYPE_STRING:
-                    return Float.parseFloat(valueString);
+                case TYPE_STRING_INTERNED:
+                    try {
+                        return Float.parseFloat(valueString);
+                    } catch (Exception e) {
+                        throw new XmlPullParserException("Invalid attribute " + name + ": " + e);
+                    }
                 default:
-                    throw new IOException("Invalid conversion from " + type);
+                    throw new XmlPullParserException("Invalid conversion from " + type);
             }
         }
 
-        public double getValueDouble() throws IOException {
+        public double getValueDouble() throws XmlPullParserException {
             switch (type) {
                 case TYPE_DOUBLE:
                     return valueDouble;
                 case TYPE_STRING:
-                    return Double.parseDouble(valueString);
+                case TYPE_STRING_INTERNED:
+                    try {
+                        return Double.parseDouble(valueString);
+                    } catch (Exception e) {
+                        throw new XmlPullParserException("Invalid attribute " + name + ": " + e);
+                    }
                 default:
-                    throw new IOException("Invalid conversion from " + type);
+                    throw new XmlPullParserException("Invalid conversion from " + type);
             }
         }
 
-        public boolean getValueBoolean() throws IOException {
+        public boolean getValueBoolean() throws XmlPullParserException {
             switch (type) {
                 case TYPE_BOOLEAN_TRUE:
                     return true;
                 case TYPE_BOOLEAN_FALSE:
                     return false;
                 case TYPE_STRING:
+                case TYPE_STRING_INTERNED:
                     if ("true".equalsIgnoreCase(valueString)) {
                         return true;
                     } else if ("false".equalsIgnoreCase(valueString)) {
                         return false;
                     } else {
-                        throw new IOException("Invalid boolean: " + valueString);
+                        throw new XmlPullParserException(
+                                "Invalid attribute " + name + ": " + valueString);
                     }
                 default:
-                    throw new IOException("Invalid conversion from " + type);
+                    throw new XmlPullParserException("Invalid conversion from " + type);
             }
         }
     }
@@ -865,11 +917,11 @@
     private final static char[] HEX_DIGITS =
             { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
 
-    private static int toByte(char c) throws IOException {
+    private static int toByte(char c) {
         if (c >= '0' && c <= '9') return (c - '0');
         if (c >= 'A' && c <= 'F') return (c - 'A' + 10);
         if (c >= 'a' && c <= 'f') return (c - 'a' + 10);
-        throw new IOException("Invalid hex char '" + c + "'");
+        throw new IllegalArgumentException("Invalid hex char '" + c + "'");
     }
 
     static String bytesToHexString(byte[] value) {
@@ -884,10 +936,10 @@
         return new String(buf);
     }
 
-    static byte[] hexStringToBytes(String value) throws IOException {
+    static byte[] hexStringToBytes(String value) {
         final int length = value.length();
         if (length % 2 != 0) {
-            throw new IOException("Invalid hex length " + length);
+            throw new IllegalArgumentException("Invalid hex length " + length);
         }
         byte[] buffer = new byte[length / 2];
         for (int i = 0; i < length; i += 2) {
diff --git a/core/java/com/android/internal/util/XmlUtils.java b/core/java/com/android/internal/util/XmlUtils.java
index cdd0e04..1e79aef 100644
--- a/core/java/com/android/internal/util/XmlUtils.java
+++ b/core/java/com/android/internal/util/XmlUtils.java
@@ -140,87 +140,95 @@
         }
 
         @Override
-        public byte[] getAttributeBytesHex(String namespace, String name) throws IOException {
+        public byte[] getAttributeBytesHex(String namespace, String name)
+                throws XmlPullParserException {
             try {
                 return HexDump.hexStringToByteArray(getAttributeValue(namespace, name));
             } catch (Exception e) {
-                throw new IOException("Invalid attribute " + name, e);
+                throw new XmlPullParserException("Invalid attribute " + name + ": " + e);
             }
         }
 
         @Override
-        public byte[] getAttributeBytesBase64(String namespace, String name) throws IOException {
+        public byte[] getAttributeBytesBase64(String namespace, String name)
+                throws XmlPullParserException {
             try {
                 return Base64.decode(getAttributeValue(namespace, name), Base64.NO_WRAP);
             } catch (Exception e) {
-                throw new IOException("Invalid attribute " + name, e);
+                throw new XmlPullParserException("Invalid attribute " + name + ": " + e);
             }
         }
 
         @Override
-        public int getAttributeInt(String namespace, String name) throws IOException {
+        public int getAttributeInt(String namespace, String name)
+                throws XmlPullParserException {
             try {
                 return Integer.parseInt(getAttributeValue(namespace, name));
-            } catch (NumberFormatException e) {
-                throw new IOException("Invalid attribute " + name, e);
+            } catch (Exception e) {
+                throw new XmlPullParserException("Invalid attribute " + name + ": " + e);
             }
         }
 
         @Override
-        public int getAttributeIntHex(String namespace, String name) throws IOException {
+        public int getAttributeIntHex(String namespace, String name)
+                throws XmlPullParserException {
             try {
                 return Integer.parseInt(getAttributeValue(namespace, name), 16);
-            } catch (NumberFormatException e) {
-                throw new IOException("Invalid attribute " + name, e);
+            } catch (Exception e) {
+                throw new XmlPullParserException("Invalid attribute " + name + ": " + e);
             }
         }
 
         @Override
-        public long getAttributeLong(String namespace, String name) throws IOException {
+        public long getAttributeLong(String namespace, String name)
+                throws XmlPullParserException {
             try {
                 return Long.parseLong(getAttributeValue(namespace, name));
-            } catch (NumberFormatException e) {
-                throw new IOException("Invalid attribute " + name, e);
+            } catch (Exception e) {
+                throw new XmlPullParserException("Invalid attribute " + name + ": " + e);
             }
         }
 
         @Override
-        public long getAttributeLongHex(String namespace, String name) throws IOException {
+        public long getAttributeLongHex(String namespace, String name)
+                throws XmlPullParserException {
             try {
                 return Long.parseLong(getAttributeValue(namespace, name), 16);
-            } catch (NumberFormatException e) {
-                throw new IOException("Invalid attribute " + name, e);
+            } catch (Exception e) {
+                throw new XmlPullParserException("Invalid attribute " + name + ": " + e);
             }
         }
 
         @Override
-        public float getAttributeFloat(String namespace, String name) throws IOException {
+        public float getAttributeFloat(String namespace, String name)
+                throws XmlPullParserException {
             try {
                 return Float.parseFloat(getAttributeValue(namespace, name));
-            } catch (NumberFormatException e) {
-                throw new IOException("Invalid attribute " + name, e);
+            } catch (Exception e) {
+                throw new XmlPullParserException("Invalid attribute " + name + ": " + e);
             }
         }
 
         @Override
-        public double getAttributeDouble(String namespace, String name) throws IOException {
+        public double getAttributeDouble(String namespace, String name)
+                throws XmlPullParserException {
             try {
                 return Double.parseDouble(getAttributeValue(namespace, name));
-            } catch (NumberFormatException e) {
-                throw new IOException("Invalid attribute " + name, e);
+            } catch (Exception e) {
+                throw new XmlPullParserException("Invalid attribute " + name + ": " + e);
             }
         }
 
         @Override
-        public boolean getAttributeBoolean(String namespace, String name) throws IOException {
+        public boolean getAttributeBoolean(String namespace, String name)
+                throws XmlPullParserException {
             final String value = getAttributeValue(namespace, name);
             if ("true".equalsIgnoreCase(value)) {
                 return true;
             } else if ("false".equalsIgnoreCase(value)) {
                 return false;
             } else {
-                throw new IOException("Invalid attribute " + name,
-                        new IllegalArgumentException("For input string: \"" + value + "\""));
+                throw new XmlPullParserException("Invalid attribute " + name + ": " + value);
             }
         }
     }
diff --git a/core/tests/coretests/src/android/util/XmlTest.java b/core/tests/coretests/src/android/util/XmlTest.java
index a30381a..5ae17c4 100644
--- a/core/tests/coretests/src/android/util/XmlTest.java
+++ b/core/tests/coretests/src/android/util/XmlTest.java
@@ -135,6 +135,10 @@
         outer.putDoubleArray("double[]", new double[] { 43d, 44d, 45d });
         outer.putStringArray("string[]", new String[] { "foo", "bar", "baz" });
 
+        outer.putString("nullString", null);
+        outer.putObject("nullObject", null);
+        outer.putIntArray("nullArray", null);
+
         final PersistableBundle nested = new PersistableBundle();
         nested.putString("nested_key", "nested_value");
         outer.putPersistableBundle("nested", nested);
@@ -197,7 +201,9 @@
     }
 
     private static final String TEST_STRING = "com.example";
+    private static final String TEST_STRING_EMPTY = "";
     private static final byte[] TEST_BYTES = new byte[] { 0, 1, 2, 3, 4, 3, 2, 1, 0 };
+    private static final byte[] TEST_BYTES_EMPTY = new byte[0];
 
     private static void doVerifyWrite(TypedXmlSerializer out) throws Exception {
         out.startDocument(StandardCharsets.UTF_8.name(), true);
@@ -206,9 +212,11 @@
             out.startTag(null, "two");
             {
                 out.attribute(null, "string", TEST_STRING);
-                out.attribute(null, "stringNumber", "49");
+                out.attribute(null, "stringEmpty", TEST_STRING_EMPTY);
                 out.attributeBytesHex(null, "bytesHex", TEST_BYTES);
+                out.attributeBytesHex(null, "bytesHexEmpty", TEST_BYTES_EMPTY);
                 out.attributeBytesBase64(null, "bytesBase64", TEST_BYTES);
+                out.attributeBytesBase64(null, "bytesBase64Empty", TEST_BYTES_EMPTY);
                 out.attributeInt(null, "int", 43);
                 out.attributeIntHex(null, "intHex", 44);
                 out.attributeLong(null, "long", 45L);
@@ -216,6 +224,7 @@
                 out.attributeFloat(null, "float", 47f);
                 out.attributeDouble(null, "double", 48d);
                 out.attributeBoolean(null, "boolean", true);
+                out.attribute(null, "stringNumber", "49");
             }
             out.endTag(null, "two");
 
@@ -241,10 +250,20 @@
         {
             assertNext(in, START_TAG, "two", 2);
             {
-                assertEquals(11, in.getAttributeCount());
-                assertEquals(TEST_STRING, in.getAttributeValue(null, "string"));
-                assertArrayEquals(TEST_BYTES, in.getAttributeBytesHex(null, "bytesHex"));
-                assertArrayEquals(TEST_BYTES, in.getAttributeBytesBase64(null, "bytesBase64"));
+                assertEquals(14, in.getAttributeCount());
+                assertEquals(TEST_STRING,
+                        in.getAttributeValue(null, "string"));
+                assertEquals(TEST_STRING_EMPTY,
+                        in.getAttributeValue(null, "stringEmpty"));
+                assertArrayEquals(TEST_BYTES,
+                        in.getAttributeBytesHex(null, "bytesHex"));
+                assertArrayEquals(TEST_BYTES_EMPTY,
+                        in.getAttributeBytesHex(null, "bytesHexEmpty"));
+                assertArrayEquals(TEST_BYTES,
+                        in.getAttributeBytesBase64(null, "bytesBase64"));
+                assertArrayEquals(TEST_BYTES_EMPTY,
+                        in.getAttributeBytesBase64(null, "bytesBase64Empty"));
+
                 assertEquals(43, in.getAttributeInt(null, "int"));
                 assertEquals(44, in.getAttributeIntHex(null, "intHex"));
                 assertEquals(45L, in.getAttributeLong(null, "long"));
diff --git a/core/tests/utiltests/src/com/android/internal/util/XmlUtilsTest.java b/core/tests/utiltests/src/com/android/internal/util/XmlUtilsTest.java
index 27f3596..06e9759 100644
--- a/core/tests/utiltests/src/com/android/internal/util/XmlUtilsTest.java
+++ b/core/tests/utiltests/src/com/android/internal/util/XmlUtilsTest.java
@@ -18,8 +18,12 @@
 
 import static org.junit.Assert.assertArrayEquals;
 
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
 import android.util.Xml;
 
+import junit.framework.TestCase;
+
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
 import java.io.InputStream;
@@ -27,11 +31,6 @@
 import java.util.HashMap;
 import java.util.Map;
 
-import junit.framework.TestCase;
-
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlSerializer;
-
 public class XmlUtilsTest extends TestCase {
 
     // https://code.google.com/p/android/issues/detail?id=63717
@@ -52,14 +51,14 @@
         byte[] testByteArray = {0x1 , 0xa, 0xb, 0x9, 0x34, (byte) 0xaa, (byte) 0xba, (byte) 0x99};
 
         ByteArrayOutputStream baos = new ByteArrayOutputStream(1024);
-        XmlSerializer serializer = new FastXmlSerializer();
+        TypedXmlSerializer serializer = Xml.newFastSerializer();
         serializer.setOutput(baos, StandardCharsets.UTF_8.name());
         serializer.startDocument(null, true);
         XmlUtils.writeValueXml(testByteArray,  "testByteArray", serializer);
         serializer.endDocument();
 
         InputStream bais = new ByteArrayInputStream(baos.toByteArray());
-        XmlPullParser pullParser = Xml.newPullParser();
+        TypedXmlPullParser pullParser = Xml.newFastPullParser();
         pullParser.setInput(bais, StandardCharsets.UTF_8.name());
         String[] name = new String[1];
         byte[] testByteArrayDeserialized = (byte[]) XmlUtils.readValueXml(pullParser, name);