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
This commit is contained in:
Jeff Sharkey
2020-11-13 10:24:55 -07:00
parent bfdd1a33dd
commit 03dec84123
6 changed files with 213 additions and 110 deletions

View File

@@ -20,8 +20,7 @@ import android.annotation.NonNull;
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 @@ import java.io.IOException;
*/
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

View File

@@ -36,21 +36,21 @@ public interface TypedXmlSerializer extends XmlSerializer {
* 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

View File

@@ -418,13 +418,14 @@ public final class BinaryXmlPullParser implements TypedXmlPullParser {
* 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 @@ public final class BinaryXmlPullParser implements TypedXmlPullParser {
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 @@ public final class BinaryXmlPullParser implements TypedXmlPullParser {
}
@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 final class BinaryXmlPullParser implements TypedXmlPullParser {
}
}
public @Nullable byte[] getValueBytesHex() throws IOException {
public @Nullable byte[] getValueBytesHex() throws XmlPullParserException {
switch (type) {
case TYPE_NULL:
return null;
@@ -750,13 +760,18 @@ public final class BinaryXmlPullParser implements TypedXmlPullParser {
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 @@ public final class BinaryXmlPullParser implements TypedXmlPullParser {
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 @@ public final class BinaryXmlPullParser implements TypedXmlPullParser {
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 @@ public final class BinaryXmlPullParser implements TypedXmlPullParser {
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) {

View File

@@ -140,87 +140,95 @@ public class XmlUtils {
}
@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);
}
}
}

View File

@@ -135,6 +135,10 @@ public class XmlTest {
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 @@ public class XmlTest {
}
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 @@ public class XmlTest {
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 @@ public class XmlTest {
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 @@ public class XmlTest {
{
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"));

View File

@@ -18,8 +18,12 @@ package com.android.internal.util;
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.nio.charset.StandardCharsets;
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 @@ public class XmlUtilsTest extends TestCase {
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);