Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 78e84716 authored by Jeff Sharkey's avatar Jeff Sharkey
Browse files

Mitigate performance TypedXml regression.

When an attribute value is missing, we can immediately return the
default value instead of paying the cost of throwing an exception.

To bring this benefit to both human-readable and binary XML, this
is implemented by adding a new getAttributeIndex() lookup method
which is then used by default methods.

readExtraAttributes() also appears to be parsing a string list as
a boolean value (which seems very broken), so we add hand-rolled
parsing logic to avoid paying the cost of an exception.  Someone
should follow up in the future to investigate if there's an actual
bug here.

Bug: 174544002
Test: manual
Change-Id: Id313d18000e345ca1acd3c66d2c0c0fc44769b71
parent 83eeb33c
Loading
Loading
Loading
Loading
+146 −27
Original line number Diff line number Diff line
@@ -29,68 +29,169 @@ import org.xmlpull.v1.XmlPullParserException;
 * @hide
 */
public interface TypedXmlPullParser extends XmlPullParser {
    /**
     * @return index of requested attribute, otherwise {@code -1} if undefined
     */
    default int getAttributeIndex(@Nullable String namespace, @NonNull String name) {
        final boolean namespaceNull = (namespace == null);
        final int count = getAttributeCount();
        for (int i = 0; i < count; i++) {
            if ((namespaceNull || namespace.equals(getAttributeNamespace(i)))
                    && name.equals(getAttributeName(i))) {
                return i;
            }
        }
        return -1;
    }

    /**
     * @return index of requested attribute
     * @throws XmlPullParserException if the value is undefined
     */
    default int getAttributeIndexOrThrow(@Nullable String namespace, @NonNull String name)
            throws XmlPullParserException {
        final int index = getAttributeIndex(namespace, name);
        if (index == -1) {
            throw new XmlPullParserException("Missing attribute " + name);
        } else {
            return index;
        }
    }

    /**
     * @return decoded strongly-typed {@link #getAttributeValue}
     * @throws XmlPullParserException if the value is malformed
     */
    @NonNull byte[] getAttributeBytesHex(int index) throws XmlPullParserException;

    /**
     * @return decoded strongly-typed {@link #getAttributeValue}
     * @throws XmlPullParserException if the value is malformed
     */
    @NonNull byte[] getAttributeBytesBase64(int index) throws XmlPullParserException;

    /**
     * @return decoded strongly-typed {@link #getAttributeValue}
     * @throws XmlPullParserException if the value is malformed
     */
    int getAttributeInt(int index) throws XmlPullParserException;

    /**
     * @return decoded strongly-typed {@link #getAttributeValue}
     * @throws XmlPullParserException if the value is malformed
     */
    int getAttributeIntHex(int index) throws XmlPullParserException;

    /**
     * @return decoded strongly-typed {@link #getAttributeValue}
     * @throws XmlPullParserException if the value is malformed
     */
    long getAttributeLong(int index) throws XmlPullParserException;

    /**
     * @return decoded strongly-typed {@link #getAttributeValue}
     * @throws XmlPullParserException if the value is malformed
     */
    long getAttributeLongHex(int index) throws XmlPullParserException;

    /**
     * @return decoded strongly-typed {@link #getAttributeValue}
     * @throws XmlPullParserException if the value is malformed
     */
    float getAttributeFloat(int index) throws XmlPullParserException;

    /**
     * @return decoded strongly-typed {@link #getAttributeValue}
     * @throws XmlPullParserException if the value is malformed
     */
    double getAttributeDouble(int index) throws XmlPullParserException;

    /**
     * @return decoded strongly-typed {@link #getAttributeValue}
     * @throws XmlPullParserException if the value is malformed
     */
    boolean getAttributeBoolean(int index) throws XmlPullParserException;

    /**
     * @return decoded strongly-typed {@link #getAttributeValue}
     * @throws XmlPullParserException if the value is malformed or undefined
     */
    @NonNull byte[] getAttributeBytesHex(@Nullable String namespace, @NonNull String name)
            throws XmlPullParserException;
    default @NonNull byte[] getAttributeBytesHex(@Nullable String namespace,
            @NonNull String name) throws XmlPullParserException {
        return getAttributeBytesHex(getAttributeIndexOrThrow(namespace, name));
    }

    /**
     * @return decoded strongly-typed {@link #getAttributeValue}
     * @throws XmlPullParserException if the value is malformed or undefined
     */
    @NonNull byte[] getAttributeBytesBase64(@Nullable String namespace, @NonNull String name)
            throws XmlPullParserException;
    default @NonNull byte[] getAttributeBytesBase64(@Nullable String namespace,
            @NonNull String name) throws XmlPullParserException {
        return getAttributeBytesBase64(getAttributeIndexOrThrow(namespace, name));
    }

    /**
     * @return decoded strongly-typed {@link #getAttributeValue}
     * @throws XmlPullParserException if the value is malformed or undefined
     */
    int getAttributeInt(@Nullable String namespace, @NonNull String name)
            throws XmlPullParserException;
    default int getAttributeInt(@Nullable String namespace, @NonNull String name)
            throws XmlPullParserException {
        return getAttributeInt(getAttributeIndexOrThrow(namespace, name));
    }

    /**
     * @return decoded strongly-typed {@link #getAttributeValue}
     * @throws XmlPullParserException if the value is malformed or undefined
     */
    int getAttributeIntHex(@Nullable String namespace, @NonNull String name)
            throws XmlPullParserException;
    default int getAttributeIntHex(@Nullable String namespace, @NonNull String name)
            throws XmlPullParserException {
        return getAttributeIntHex(getAttributeIndexOrThrow(namespace, name));
    }

    /**
     * @return decoded strongly-typed {@link #getAttributeValue}
     * @throws XmlPullParserException if the value is malformed or undefined
     */
    long getAttributeLong(@Nullable String namespace, @NonNull String name)
            throws XmlPullParserException;
    default long getAttributeLong(@Nullable String namespace, @NonNull String name)
            throws XmlPullParserException {
        return getAttributeLong(getAttributeIndexOrThrow(namespace, name));
    }

    /**
     * @return decoded strongly-typed {@link #getAttributeValue}
     * @throws XmlPullParserException if the value is malformed or undefined
     */
    long getAttributeLongHex(@Nullable String namespace, @NonNull String name)
            throws XmlPullParserException;
    default long getAttributeLongHex(@Nullable String namespace, @NonNull String name)
            throws XmlPullParserException {
        return getAttributeLongHex(getAttributeIndexOrThrow(namespace, name));
    }

    /**
     * @return decoded strongly-typed {@link #getAttributeValue}
     * @throws XmlPullParserException if the value is malformed or undefined
     */
    float getAttributeFloat(@Nullable String namespace, @NonNull String name)
            throws XmlPullParserException;
    default float getAttributeFloat(@Nullable String namespace, @NonNull String name)
            throws XmlPullParserException {
        return getAttributeFloat(getAttributeIndexOrThrow(namespace, name));
    }

    /**
     * @return decoded strongly-typed {@link #getAttributeValue}
     * @throws XmlPullParserException if the value is malformed or undefined
     */
    double getAttributeDouble(@Nullable String namespace, @NonNull String name)
            throws XmlPullParserException;
    default double getAttributeDouble(@Nullable String namespace, @NonNull String name)
            throws XmlPullParserException {
        return getAttributeDouble(getAttributeIndexOrThrow(namespace, name));
    }

    /**
     * @return decoded strongly-typed {@link #getAttributeValue}
     * @throws XmlPullParserException if the value is malformed or undefined
     */
    boolean getAttributeBoolean(@Nullable String namespace, @NonNull String name)
            throws XmlPullParserException;
    default boolean getAttributeBoolean(@Nullable String namespace, @NonNull String name)
            throws XmlPullParserException {
        return getAttributeBoolean(getAttributeIndexOrThrow(namespace, name));
    }

    /**
     * @return decoded strongly-typed {@link #getAttributeValue}, otherwise
@@ -98,8 +199,10 @@ public interface TypedXmlPullParser extends XmlPullParser {
     */
    default @Nullable byte[] getAttributeBytesHex(@Nullable String namespace,
            @NonNull String name, @Nullable byte[] defaultValue) {
        final int index = getAttributeIndex(namespace, name);
        if (index == -1) return defaultValue;
        try {
            return getAttributeBytesHex(namespace, name);
            return getAttributeBytesHex(index);
        } catch (Exception ignored) {
            return defaultValue;
        }
@@ -111,8 +214,10 @@ public interface TypedXmlPullParser extends XmlPullParser {
     */
    default @Nullable byte[] getAttributeBytesBase64(@Nullable String namespace,
            @NonNull String name, @Nullable byte[] defaultValue) {
        final int index = getAttributeIndex(namespace, name);
        if (index == -1) return defaultValue;
        try {
            return getAttributeBytesBase64(namespace, name);
            return getAttributeBytesBase64(index);
        } catch (Exception ignored) {
            return defaultValue;
        }
@@ -124,8 +229,10 @@ public interface TypedXmlPullParser extends XmlPullParser {
     */
    default int getAttributeInt(@Nullable String namespace, @NonNull String name,
            int defaultValue) {
        final int index = getAttributeIndex(namespace, name);
        if (index == -1) return defaultValue;
        try {
            return getAttributeInt(namespace, name);
            return getAttributeInt(index);
        } catch (Exception ignored) {
            return defaultValue;
        }
@@ -137,8 +244,10 @@ public interface TypedXmlPullParser extends XmlPullParser {
     */
    default int getAttributeIntHex(@Nullable String namespace, @NonNull String name,
            int defaultValue) {
        final int index = getAttributeIndex(namespace, name);
        if (index == -1) return defaultValue;
        try {
            return getAttributeIntHex(namespace, name);
            return getAttributeIntHex(index);
        } catch (Exception ignored) {
            return defaultValue;
        }
@@ -150,8 +259,10 @@ public interface TypedXmlPullParser extends XmlPullParser {
     */
    default long getAttributeLong(@Nullable String namespace, @NonNull String name,
            long defaultValue) {
        final int index = getAttributeIndex(namespace, name);
        if (index == -1) return defaultValue;
        try {
            return getAttributeLong(namespace, name);
            return getAttributeLong(index);
        } catch (Exception ignored) {
            return defaultValue;
        }
@@ -163,8 +274,10 @@ public interface TypedXmlPullParser extends XmlPullParser {
     */
    default long getAttributeLongHex(@Nullable String namespace, @NonNull String name,
            long defaultValue) {
        final int index = getAttributeIndex(namespace, name);
        if (index == -1) return defaultValue;
        try {
            return getAttributeLongHex(namespace, name);
            return getAttributeLongHex(index);
        } catch (Exception ignored) {
            return defaultValue;
        }
@@ -176,8 +289,10 @@ public interface TypedXmlPullParser extends XmlPullParser {
     */
    default float getAttributeFloat(@Nullable String namespace, @NonNull String name,
            float defaultValue) {
        final int index = getAttributeIndex(namespace, name);
        if (index == -1) return defaultValue;
        try {
            return getAttributeFloat(namespace, name);
            return getAttributeFloat(index);
        } catch (Exception ignored) {
            return defaultValue;
        }
@@ -189,8 +304,10 @@ public interface TypedXmlPullParser extends XmlPullParser {
     */
    default double getAttributeDouble(@Nullable String namespace, @NonNull String name,
            double defaultValue) {
        final int index = getAttributeIndex(namespace, name);
        if (index == -1) return defaultValue;
        try {
            return getAttributeDouble(namespace, name);
            return getAttributeDouble(index);
        } catch (Exception ignored) {
            return defaultValue;
        }
@@ -202,8 +319,10 @@ public interface TypedXmlPullParser extends XmlPullParser {
     */
    default boolean getAttributeBoolean(@Nullable String namespace, @NonNull String name,
            boolean defaultValue) {
        final int index = getAttributeIndex(namespace, name);
        if (index == -1) return defaultValue;
        try {
            return getAttributeBoolean(namespace, name);
            return getAttributeBoolean(index);
        } catch (Exception ignored) {
            return defaultValue;
        }
+25 −51
Original line number Diff line number Diff line
@@ -414,29 +414,21 @@ public final class BinaryXmlPullParser implements TypedXmlPullParser {
        mAttributeCount = 0;
    }

    /**
     * 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 XmlPullParserException {
    @Override
    public int getAttributeIndex(String namespace, String name) {
        if (namespace != null && !namespace.isEmpty()) throw illegalNamespace();
        for (int i = 0; i < mAttributeCount; i++) {
            if (Objects.equals(mAttributes[i].name, name)) {
                return mAttributes[i];
                return i;
            }
        }
        throw new XmlPullParserException("Missing attribute " + name);
        return -1;
    }

    @Override
    public String getAttributeValue(String namespace, String name) {
        if (namespace != null && !namespace.isEmpty()) throw illegalNamespace();
        try {
            return findAttribute(name).getValueString();
        } catch (XmlPullParserException e) {
            // Missing attributes default to null
            return null;
        }
        final int index = getAttributeIndex(namespace, name);
        return mAttributes[index].getValueString();
    }

    @Override
@@ -445,66 +437,48 @@ public final class BinaryXmlPullParser implements TypedXmlPullParser {
    }

    @Override
    public byte[] getAttributeBytesHex(String namespace, String name)
            throws XmlPullParserException {
        if (namespace != null && !namespace.isEmpty()) throw illegalNamespace();
        return findAttribute(name).getValueBytesHex();
    public byte[] getAttributeBytesHex(int index) throws XmlPullParserException {
        return mAttributes[index].getValueBytesHex();
    }

    @Override
    public byte[] getAttributeBytesBase64(String namespace, String name)
            throws XmlPullParserException {
        if (namespace != null && !namespace.isEmpty()) throw illegalNamespace();
        return findAttribute(name).getValueBytesBase64();
    public byte[] getAttributeBytesBase64(int index) throws XmlPullParserException {
        return mAttributes[index].getValueBytesBase64();
    }

    @Override
    public int getAttributeInt(String namespace, String name)
            throws XmlPullParserException {
        if (namespace != null && !namespace.isEmpty()) throw illegalNamespace();
        return findAttribute(name).getValueInt();
    public int getAttributeInt(int index) throws XmlPullParserException {
        return mAttributes[index].getValueInt();
    }

    @Override
    public int getAttributeIntHex(String namespace, String name)
            throws XmlPullParserException {
        if (namespace != null && !namespace.isEmpty()) throw illegalNamespace();
        return findAttribute(name).getValueIntHex();
    public int getAttributeIntHex(int index) throws XmlPullParserException {
        return mAttributes[index].getValueIntHex();
    }

    @Override
    public long getAttributeLong(String namespace, String name)
            throws XmlPullParserException {
        if (namespace != null && !namespace.isEmpty()) throw illegalNamespace();
        return findAttribute(name).getValueLong();
    public long getAttributeLong(int index) throws XmlPullParserException {
        return mAttributes[index].getValueLong();
    }

    @Override
    public long getAttributeLongHex(String namespace, String name)
            throws XmlPullParserException {
        if (namespace != null && !namespace.isEmpty()) throw illegalNamespace();
        return findAttribute(name).getValueLongHex();
    public long getAttributeLongHex(int index) throws XmlPullParserException {
        return mAttributes[index].getValueLongHex();
    }

    @Override
    public float getAttributeFloat(String namespace, String name)
            throws XmlPullParserException {
        if (namespace != null && !namespace.isEmpty()) throw illegalNamespace();
        return findAttribute(name).getValueFloat();
    public float getAttributeFloat(int index) throws XmlPullParserException {
        return mAttributes[index].getValueFloat();
    }

    @Override
    public double getAttributeDouble(String namespace, String name)
            throws XmlPullParserException {
        if (namespace != null && !namespace.isEmpty()) throw illegalNamespace();
        return findAttribute(name).getValueDouble();
    public double getAttributeDouble(int index) throws XmlPullParserException {
        return mAttributes[index].getValueDouble();
    }

    @Override
    public boolean getAttributeBoolean(String namespace, String name)
            throws XmlPullParserException {
        if (namespace != null && !namespace.isEmpty()) throw illegalNamespace();
        return findAttribute(name).getValueBoolean();
    public boolean getAttributeBoolean(int index) throws XmlPullParserException {
        return mAttributes[index].getValueBoolean();
    }

    @Override
+36 −27
Original line number Diff line number Diff line
@@ -142,95 +142,104 @@ public class XmlUtils {
        }

        @Override
        public byte[] getAttributeBytesHex(String namespace, String name)
        public byte[] getAttributeBytesHex(int index)
                throws XmlPullParserException {
            try {
                return HexDump.hexStringToByteArray(getAttributeValue(namespace, name));
                return HexDump.hexStringToByteArray(getAttributeValue(index));
            } catch (Exception e) {
                throw new XmlPullParserException("Invalid attribute " + name + ": " + e);
                throw new XmlPullParserException(
                        "Invalid attribute " + getAttributeName(index) + ": " + e);
            }
        }

        @Override
        public byte[] getAttributeBytesBase64(String namespace, String name)
        public byte[] getAttributeBytesBase64(int index)
                throws XmlPullParserException {
            try {
                return Base64.decode(getAttributeValue(namespace, name), Base64.NO_WRAP);
                return Base64.decode(getAttributeValue(index), Base64.NO_WRAP);
            } catch (Exception e) {
                throw new XmlPullParserException("Invalid attribute " + name + ": " + e);
                throw new XmlPullParserException(
                        "Invalid attribute " + getAttributeName(index) + ": " + e);
            }
        }

        @Override
        public int getAttributeInt(String namespace, String name)
        public int getAttributeInt(int index)
                throws XmlPullParserException {
            try {
                return Integer.parseInt(getAttributeValue(namespace, name));
                return Integer.parseInt(getAttributeValue(index));
            } catch (Exception e) {
                throw new XmlPullParserException("Invalid attribute " + name + ": " + e);
                throw new XmlPullParserException(
                        "Invalid attribute " + getAttributeName(index) + ": " + e);
            }
        }

        @Override
        public int getAttributeIntHex(String namespace, String name)
        public int getAttributeIntHex(int index)
                throws XmlPullParserException {
            try {
                return Integer.parseInt(getAttributeValue(namespace, name), 16);
                return Integer.parseInt(getAttributeValue(index), 16);
            } catch (Exception e) {
                throw new XmlPullParserException("Invalid attribute " + name + ": " + e);
                throw new XmlPullParserException(
                        "Invalid attribute " + getAttributeName(index) + ": " + e);
            }
        }

        @Override
        public long getAttributeLong(String namespace, String name)
        public long getAttributeLong(int index)
                throws XmlPullParserException {
            try {
                return Long.parseLong(getAttributeValue(namespace, name));
                return Long.parseLong(getAttributeValue(index));
            } catch (Exception e) {
                throw new XmlPullParserException("Invalid attribute " + name + ": " + e);
                throw new XmlPullParserException(
                        "Invalid attribute " + getAttributeName(index) + ": " + e);
            }
        }

        @Override
        public long getAttributeLongHex(String namespace, String name)
        public long getAttributeLongHex(int index)
                throws XmlPullParserException {
            try {
                return Long.parseLong(getAttributeValue(namespace, name), 16);
                return Long.parseLong(getAttributeValue(index), 16);
            } catch (Exception e) {
                throw new XmlPullParserException("Invalid attribute " + name + ": " + e);
                throw new XmlPullParserException(
                        "Invalid attribute " + getAttributeName(index) + ": " + e);
            }
        }

        @Override
        public float getAttributeFloat(String namespace, String name)
        public float getAttributeFloat(int index)
                throws XmlPullParserException {
            try {
                return Float.parseFloat(getAttributeValue(namespace, name));
                return Float.parseFloat(getAttributeValue(index));
            } catch (Exception e) {
                throw new XmlPullParserException("Invalid attribute " + name + ": " + e);
                throw new XmlPullParserException(
                        "Invalid attribute " + getAttributeName(index) + ": " + e);
            }
        }

        @Override
        public double getAttributeDouble(String namespace, String name)
        public double getAttributeDouble(int index)
                throws XmlPullParserException {
            try {
                return Double.parseDouble(getAttributeValue(namespace, name));
                return Double.parseDouble(getAttributeValue(index));
            } catch (Exception e) {
                throw new XmlPullParserException("Invalid attribute " + name + ": " + e);
                throw new XmlPullParserException(
                        "Invalid attribute " + getAttributeName(index) + ": " + e);
            }
        }

        @Override
        public boolean getAttributeBoolean(String namespace, String name)
        public boolean getAttributeBoolean(int index)
                throws XmlPullParserException {
            final String value = getAttributeValue(namespace, name);
            final String value = getAttributeValue(index);
            if ("true".equalsIgnoreCase(value)) {
                return true;
            } else if ("false".equalsIgnoreCase(value)) {
                return false;
            } else {
                throw new XmlPullParserException("Invalid attribute " + name + ": " + value);
                throw new XmlPullParserException(
                        "Invalid attribute " + getAttributeName(index) + ": " + value);
            }
        }
    }
+10 −1
Original line number Diff line number Diff line
@@ -9180,7 +9180,16 @@ public class NotificationManagerService extends SystemService {
        @Override
        protected void readExtraAttributes(String tag, TypedXmlPullParser parser, int userId)
                throws IOException {
            boolean userSet = parser.getAttributeBoolean(null, ATT_USER_SET, false);
            // TODO: this logic looks broken, since it's trying to convert a
            // list into a boolean; for now we preserve the old parsing behavior
            // to avoid a performance regression, but someone should investigate
            final String value = parser.getAttributeValue(null, ATT_USER_SET);
            final boolean userSet;
            if (TextUtils.isEmpty(value)) {
                userSet = false;
            } else {
                userSet = Boolean.parseBoolean(value);
            }
            setUserSet(userId, userSet);
        }