Loading core/api/system-current.txt +13 −0 Original line number Diff line number Diff line Loading @@ -11471,6 +11471,19 @@ package android.os.storage { } package android.os.vibrator.persistence { @FlaggedApi("android.os.vibrator.vibration_xml_apis") public final class ParsedVibration { method @FlaggedApi("android.os.vibrator.vibration_xml_apis") @Nullable public android.os.VibrationEffect resolve(@NonNull android.os.Vibrator); } @FlaggedApi("android.os.vibrator.vibration_xml_apis") public final class VibrationXmlParser { method @FlaggedApi("android.os.vibrator.vibration_xml_apis") @NonNull public static android.os.vibrator.persistence.ParsedVibration parse(@NonNull java.io.InputStream) throws java.io.IOException; method @FlaggedApi("android.os.vibrator.vibration_xml_apis") @NonNull public static android.os.VibrationEffect parseVibrationEffect(@NonNull java.io.InputStream) throws java.io.IOException; } } package android.permission { public final class AdminPermissionControlParams implements android.os.Parcelable { core/api/test-current.txt +11 −8 Original line number Diff line number Diff line Loading @@ -2761,21 +2761,24 @@ package android.os.vibrator { package android.os.vibrator.persistence { public class ParsedVibration { method @NonNull public java.util.List<android.os.VibrationEffect> getVibrationEffects(); method @Nullable public android.os.VibrationEffect resolve(@NonNull android.os.Vibrator); @FlaggedApi("android.os.vibrator.vibration_xml_apis") public final class ParsedVibration { ctor public ParsedVibration(@NonNull java.util.List<android.os.VibrationEffect>); method @FlaggedApi("android.os.vibrator.vibration_xml_apis") @Nullable public android.os.VibrationEffect resolve(@NonNull android.os.Vibrator); } public final class VibrationXmlParser { method @Nullable public static android.os.vibrator.persistence.ParsedVibration parseDocument(@NonNull java.io.Reader) throws java.io.IOException; method @Nullable public static android.os.VibrationEffect parseVibrationEffect(@NonNull java.io.Reader) throws java.io.IOException; @FlaggedApi("android.os.vibrator.vibration_xml_apis") public final class VibrationXmlParser { method @FlaggedApi("android.os.vibrator.vibration_xml_apis") @NonNull public static android.os.vibrator.persistence.ParsedVibration parse(@NonNull java.io.InputStream) throws java.io.IOException; method @FlaggedApi("android.os.vibrator.vibration_xml_apis") @NonNull public static android.os.VibrationEffect parseVibrationEffect(@NonNull java.io.InputStream) throws java.io.IOException; } public static final class VibrationXmlParser.ParseFailedException extends java.io.IOException { } public final class VibrationXmlSerializer { method public static void serialize(@NonNull android.os.VibrationEffect, @NonNull java.io.Writer) throws java.io.IOException, android.os.vibrator.persistence.VibrationXmlSerializer.SerializationFailedException; method public static void serialize(@NonNull android.os.VibrationEffect, @NonNull java.io.Writer) throws java.io.IOException; } public static final class VibrationXmlSerializer.SerializationFailedException extends java.lang.RuntimeException { public static final class VibrationXmlSerializer.SerializationFailedException extends java.io.IOException { } } Loading core/java/android/os/vibrator/flags.aconfig +10 −0 Original line number Diff line number Diff line Loading @@ -42,3 +42,13 @@ flag { purpose: PURPOSE_BUGFIX } } flag { namespace: "haptics" name: "vibration_xml_apis" description: "Enabled System APIs for vibration effect XML parser and serializer" bug: "347273158" metadata { purpose: PURPOSE_FEATURE } } core/java/android/os/vibrator/persistence/ParsedVibration.java +35 −26 Original line number Diff line number Diff line Loading @@ -16,31 +16,35 @@ package android.os.vibrator.persistence; import static android.os.vibrator.Flags.FLAG_VIBRATION_XML_APIS; import android.annotation.FlaggedApi; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SuppressLint; import android.annotation.SystemApi; import android.annotation.TestApi; import android.os.VibrationEffect; import android.os.Vibrator; import android.os.VibratorInfo; import com.android.internal.annotations.VisibleForTesting; import java.util.Collections; import java.util.List; import java.util.Objects; /** * The result of parsing a serialized vibration, which can be define by one or more * {@link VibrationEffect} and a resolution method. * The result of parsing a serialized vibration. * * @see VibrationXmlParser * * @hide */ @TestApi @SuppressLint("UnflaggedApi") // @TestApi without associated feature. public class ParsedVibration { @TestApi // This was used in CTS before the flag was introduced. @SystemApi @FlaggedApi(FLAG_VIBRATION_XML_APIS) public final class ParsedVibration { private final List<VibrationEffect> mEffects; /** @hide */ @TestApi public ParsedVibration(@NonNull List<VibrationEffect> effects) { mEffects = effects; } Loading @@ -49,40 +53,28 @@ public class ParsedVibration { public ParsedVibration(@NonNull VibrationEffect effect) { mEffects = List.of(effect); } /** * Returns the first parsed vibration supported by {@code vibrator}, or {@code null} if none of * the parsed vibrations are supported. * * @hide */ @TestApi @TestApi // This was used in CTS before the flag was introduced. @SystemApi @FlaggedApi(FLAG_VIBRATION_XML_APIS) @Nullable public VibrationEffect resolve(@NonNull Vibrator vibrator) { return resolve(vibrator.getInfo()); } /** * Returns the parsed vibrations for testing purposes. * * <p>Real callers should not use this method. Instead, they should resolve to a * {@link VibrationEffect} via {@link #resolve(Vibrator)}. * * @hide */ @TestApi @VisibleForTesting @NonNull public List<VibrationEffect> getVibrationEffects() { return Collections.unmodifiableList(mEffects); } /** * Same as {@link #resolve(Vibrator)}, but uses {@link VibratorInfo} instead for resolving. * * @hide */ @Nullable public final VibrationEffect resolve(@NonNull VibratorInfo info) { public VibrationEffect resolve(@NonNull VibratorInfo info) { for (int i = 0; i < mEffects.size(); i++) { VibrationEffect effect = mEffects.get(i); if (info.areVibrationFeaturesSupported(effect)) { Loading @@ -91,4 +83,21 @@ public class ParsedVibration { } return null; } @Override public boolean equals(@Nullable Object o) { if (this == o) { return true; } if (!(o instanceof ParsedVibration)) { return false; } ParsedVibration other = (ParsedVibration) o; return mEffects.equals(other.mEffects); } @Override public int hashCode() { return Objects.hashCode(mEffects); } } core/java/android/os/vibrator/persistence/VibrationXmlParser.java +123 −88 Original line number Diff line number Diff line Loading @@ -16,13 +16,15 @@ package android.os.vibrator.persistence; import static android.os.vibrator.Flags.FLAG_VIBRATION_XML_APIS; import android.annotation.FlaggedApi; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SuppressLint; import android.annotation.SystemApi; import android.annotation.TestApi; import android.os.VibrationEffect; import android.util.Slog; import android.util.Xml; import com.android.internal.vibrator.persistence.VibrationEffectXmlParser; Loading @@ -36,9 +38,12 @@ import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.Reader; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.List; Loading Loading @@ -116,10 +121,10 @@ import java.util.List; * * @hide */ @TestApi @SuppressLint("UnflaggedApi") // @TestApi without associated feature. @TestApi // This was used in CTS before the flag was introduced. @SystemApi @FlaggedApi(FLAG_VIBRATION_XML_APIS) public final class VibrationXmlParser { private static final String TAG = "VibrationXmlParser"; /** * The MIME type for a xml holding a vibration. Loading Loading @@ -168,93 +173,109 @@ public final class VibrationXmlParser { } /** * Parses XML content from given input stream into a {@link VibrationEffect}. * Parses XML content from given input stream into a {@link ParsedVibration}. * * <p>It supports both the "vibration-effect" and "vibration-select" root tags. * <ul> * <li>If "vibration-effect" is the root tag, the serialization provided should contain a * valid serialization for a single vibration. * <li>If "vibration-select" is the root tag, the serialization may contain one or more * valid vibration serializations. * </ul> * * <p>After parsing, it returns a {@link ParsedVibration} that opaquely represents the parsed * vibration(s), and the caller can get a concrete {@link VibrationEffect} by resolving this * result to a specific vibrator. * * <p>This parser fails with an exception if the content of the input stream does not follow the * schema or has unsupported values. * * @return a {@link ParsedVibration} * @throws IOException error reading from given {@link InputStream} or parsing the content. * * @hide */ @TestApi // Replacing test APIs used in CTS before the flagged system APIs was introduced. @SystemApi @FlaggedApi(FLAG_VIBRATION_XML_APIS) @NonNull public static ParsedVibration parse(@NonNull InputStream inputStream) throws IOException { return parseDocument(new InputStreamReader(inputStream, StandardCharsets.UTF_8)); } /** * Parses XML content from given input stream into a single {@link VibrationEffect}. * * <p>This method parses an XML content that contains a single, complete {@link VibrationEffect} * serialization. As such, the root tag must be a "vibration" tag. * serialization. As such, the root tag must be a "vibration-effect" tag. * * <p>This parser fails silently and returns {@code null} if the content of the input stream * does not follow the schema or has unsupported values. * <p>This parser fails with an exception if the content of the input stream does not follow the * schema or has unsupported values. * * @return the {@link VibrationEffect} if parsed successfully, {@code null} otherwise. * @throws IOException error reading from given {@link Reader} * @return the parsed {@link VibrationEffect} * @throws IOException error reading from given {@link InputStream} or parsing the content. * * @hide */ @TestApi @Nullable @TestApi // Replacing test APIs used in CTS before the flagged system APIs was introduced. @SystemApi @FlaggedApi(FLAG_VIBRATION_XML_APIS) @NonNull public static VibrationEffect parseVibrationEffect(@NonNull InputStream inputStream) throws IOException { return parseVibrationEffect(new InputStreamReader(inputStream, StandardCharsets.UTF_8)); } /** * Parses XML content from given {@link Reader} into a {@link VibrationEffect}. * * <p>Same as {@link #parseVibrationEffect(InputStream)}, but with a {@link Reader}. * * @hide */ @NonNull public static VibrationEffect parseVibrationEffect(@NonNull Reader reader) throws IOException { return parseVibrationEffect(reader, /* flags= */ 0); } /** * Parses XML content from given input stream into a {@link VibrationEffect}. * * <p>This method parses an XML content that contains a single, complete {@link VibrationEffect} * serialization. As such, the root tag must be a "vibration" tag. * Parses XML content from given {@link Reader} into a {@link VibrationEffect}. * * <p>Same as {@link #parseVibrationEffect(Reader)}, with extra flags to control the parsing * behavior. * * @hide */ @Nullable @NonNull public static VibrationEffect parseVibrationEffect(@NonNull Reader reader, @Flags int flags) throws IOException { try { return parseDocumentInternal( reader, flags, VibrationXmlParser::parseVibrationEffectInternal); } catch (XmlParserException | XmlPullParserException e) { Slog.w(TAG, "Error parsing vibration XML", e); return null; } return parseDocumentInternal(reader, flags, VibrationXmlParser::parseVibrationEffectInternal); } /** * Parses XML content from given input stream into a {@link ParsedVibration}. * Parses XML content from given {@link Reader} into a {@link ParsedVibration}. * * <p>It supports both the "vibration" and "vibration-select" root tags. * <ul> * <li>If "vibration" is the root tag, the serialization provided through {@code reader} * should contain a valid serialization for a single vibration. * <li>If "vibration-select" is the root tag, the serialization may contain one or more * valid vibration serializations. * </ul> * * <p>After parsing, it returns a {@link ParsedVibration} that opaquely represents the parsed * vibration(s), and the caller can get a concrete {@link VibrationEffect} by resolving this * result to a specific vibrator. * * <p>This parser fails silently and returns {@code null} if the content of the input does not * follow the schema or has unsupported values. * * @return a {@link ParsedVibration} * @throws IOException error reading from given {@link Reader} * <p>Same as {@link #parse(InputStream)}, but with a {@link Reader}. * * @hide */ @TestApi @Nullable @NonNull public static ParsedVibration parseDocument(@NonNull Reader reader) throws IOException { return parseDocument(reader, /* flags= */ 0); } /** * Parses XML content from given input stream into a {@link ParsedVibration}. * Parses XML content from given {@link Reader} into a {@link ParsedVibration}. * * <p>Same as {@link #parseDocument(Reader)}, with extra flags to control the parsing behavior. * * @hide */ @Nullable @NonNull public static ParsedVibration parseDocument(@NonNull Reader reader, @Flags int flags) throws IOException { try { return parseDocumentInternal(reader, flags, VibrationXmlParser::parseElementInternal); } catch (XmlParserException | XmlPullParserException e) { Slog.w(TAG, "Error parsing vibration/vibration-select XML", e); return null; } } /** Loading @@ -262,7 +283,7 @@ public final class VibrationXmlParser { * {@link ParsedVibration}. * * <p>Same as {@link #parseDocument(Reader, int)}, but, instead of parsing the full XML content, * it takes a parser that points to either a <vibration-effect> or a <vibration-select> start * it takes a parser that points to either a "vibration-effect" or a "vibration-select" start * tag. No other parser position, including start of document, is considered valid. * * <p>This method parses until an end "vibration-effect" or "vibration-select" tag (depending Loading @@ -270,37 +291,22 @@ public final class VibrationXmlParser { * will point to the end tag. * * @throws IOException error parsing from given {@link TypedXmlPullParser}. * @throws VibrationXmlParserException if the XML tag cannot be parsed into a * {@link ParsedVibration}. The given {@code parser} might be pointing to a child XML tag * that caused the parser failure. * The given {@code parser} might be pointing to a child XML tag that caused the parser * failure. * * @hide */ @NonNull public static ParsedVibration parseElement(@NonNull TypedXmlPullParser parser, @Flags int flags) throws IOException, VibrationXmlParserException { throws IOException { try { return parseElementInternal(parser, flags); } catch (XmlParserException e) { throw new VibrationXmlParserException("Error parsing vibration-select.", e); } } /** * Represents an error while parsing a vibration XML input. * * @hide */ public static final class VibrationXmlParserException extends Exception { private VibrationXmlParserException(String message, Throwable cause) { super(message, cause); } private VibrationXmlParserException(String message) { super(message); throw new ParseFailedException(e); } } @NonNull private static ParsedVibration parseElementInternal( @NonNull TypedXmlPullParser parser, @Flags int flags) throws IOException, XmlParserException { Loading @@ -313,11 +319,12 @@ public final class VibrationXmlParser { case XmlConstants.TAG_VIBRATION_SELECT: return parseVibrationSelectInternal(parser, flags); default: throw new XmlParserException( "Unexpected tag name when parsing element: " + tagName); throw new ParseFailedException( "Unexpected tag " + tagName + " when parsing a vibration"); } } @NonNull private static ParsedVibration parseVibrationSelectInternal( @NonNull TypedXmlPullParser parser, @Flags int flags) throws IOException, XmlParserException { Loading @@ -332,7 +339,7 @@ public final class VibrationXmlParser { return new ParsedVibration(effects); } /** Parses a single XML element for "vibration" tag into a {@link VibrationEffect}. */ @NonNull private static VibrationEffect parseVibrationEffectInternal( @NonNull TypedXmlPullParser parser, @Flags int flags) throws IOException, XmlParserException { Loading @@ -347,9 +354,11 @@ public final class VibrationXmlParser { * This method parses a whole XML document (provided through a {@link Reader}). The root tag is * parsed as per a provided {@link ElementParser}. */ @NonNull private static <T> T parseDocumentInternal( @NonNull Reader reader, @Flags int flags, ElementParser<T> parseLogic) throws IOException, XmlParserException, XmlPullParserException { throws IOException { try { TypedXmlPullParser parser = Xml.newFastPullParser(); parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true); parser.setInput(reader); Loading @@ -364,15 +373,41 @@ public final class VibrationXmlParser { XmlReader.readDocumentEndTag(parser); return result; } catch (XmlPullParserException e) { throw new ParseFailedException("Error initializing XMLPullParser", e); } catch (XmlParserException e) { throw new ParseFailedException(e); } } /** Encapsulate a logic to parse an XML element from an open parser. */ private interface ElementParser<T> { /** Parses a single XML element starting from the current position of the {@code parser}. */ @NonNull T parse(@NonNull TypedXmlPullParser parser, @Flags int flags) throws IOException, XmlParserException; } /** * Represents an error while parsing a vibration XML input. * * @hide */ @TestApi public static final class ParseFailedException extends IOException { private ParseFailedException(String message) { super(message); } private ParseFailedException(XmlParserException parserException) { this(parserException.getMessage(), parserException); } private ParseFailedException(String message, Throwable cause) { super(message, cause); } } private VibrationXmlParser() { } } Loading
core/api/system-current.txt +13 −0 Original line number Diff line number Diff line Loading @@ -11471,6 +11471,19 @@ package android.os.storage { } package android.os.vibrator.persistence { @FlaggedApi("android.os.vibrator.vibration_xml_apis") public final class ParsedVibration { method @FlaggedApi("android.os.vibrator.vibration_xml_apis") @Nullable public android.os.VibrationEffect resolve(@NonNull android.os.Vibrator); } @FlaggedApi("android.os.vibrator.vibration_xml_apis") public final class VibrationXmlParser { method @FlaggedApi("android.os.vibrator.vibration_xml_apis") @NonNull public static android.os.vibrator.persistence.ParsedVibration parse(@NonNull java.io.InputStream) throws java.io.IOException; method @FlaggedApi("android.os.vibrator.vibration_xml_apis") @NonNull public static android.os.VibrationEffect parseVibrationEffect(@NonNull java.io.InputStream) throws java.io.IOException; } } package android.permission { public final class AdminPermissionControlParams implements android.os.Parcelable {
core/api/test-current.txt +11 −8 Original line number Diff line number Diff line Loading @@ -2761,21 +2761,24 @@ package android.os.vibrator { package android.os.vibrator.persistence { public class ParsedVibration { method @NonNull public java.util.List<android.os.VibrationEffect> getVibrationEffects(); method @Nullable public android.os.VibrationEffect resolve(@NonNull android.os.Vibrator); @FlaggedApi("android.os.vibrator.vibration_xml_apis") public final class ParsedVibration { ctor public ParsedVibration(@NonNull java.util.List<android.os.VibrationEffect>); method @FlaggedApi("android.os.vibrator.vibration_xml_apis") @Nullable public android.os.VibrationEffect resolve(@NonNull android.os.Vibrator); } public final class VibrationXmlParser { method @Nullable public static android.os.vibrator.persistence.ParsedVibration parseDocument(@NonNull java.io.Reader) throws java.io.IOException; method @Nullable public static android.os.VibrationEffect parseVibrationEffect(@NonNull java.io.Reader) throws java.io.IOException; @FlaggedApi("android.os.vibrator.vibration_xml_apis") public final class VibrationXmlParser { method @FlaggedApi("android.os.vibrator.vibration_xml_apis") @NonNull public static android.os.vibrator.persistence.ParsedVibration parse(@NonNull java.io.InputStream) throws java.io.IOException; method @FlaggedApi("android.os.vibrator.vibration_xml_apis") @NonNull public static android.os.VibrationEffect parseVibrationEffect(@NonNull java.io.InputStream) throws java.io.IOException; } public static final class VibrationXmlParser.ParseFailedException extends java.io.IOException { } public final class VibrationXmlSerializer { method public static void serialize(@NonNull android.os.VibrationEffect, @NonNull java.io.Writer) throws java.io.IOException, android.os.vibrator.persistence.VibrationXmlSerializer.SerializationFailedException; method public static void serialize(@NonNull android.os.VibrationEffect, @NonNull java.io.Writer) throws java.io.IOException; } public static final class VibrationXmlSerializer.SerializationFailedException extends java.lang.RuntimeException { public static final class VibrationXmlSerializer.SerializationFailedException extends java.io.IOException { } } Loading
core/java/android/os/vibrator/flags.aconfig +10 −0 Original line number Diff line number Diff line Loading @@ -42,3 +42,13 @@ flag { purpose: PURPOSE_BUGFIX } } flag { namespace: "haptics" name: "vibration_xml_apis" description: "Enabled System APIs for vibration effect XML parser and serializer" bug: "347273158" metadata { purpose: PURPOSE_FEATURE } }
core/java/android/os/vibrator/persistence/ParsedVibration.java +35 −26 Original line number Diff line number Diff line Loading @@ -16,31 +16,35 @@ package android.os.vibrator.persistence; import static android.os.vibrator.Flags.FLAG_VIBRATION_XML_APIS; import android.annotation.FlaggedApi; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SuppressLint; import android.annotation.SystemApi; import android.annotation.TestApi; import android.os.VibrationEffect; import android.os.Vibrator; import android.os.VibratorInfo; import com.android.internal.annotations.VisibleForTesting; import java.util.Collections; import java.util.List; import java.util.Objects; /** * The result of parsing a serialized vibration, which can be define by one or more * {@link VibrationEffect} and a resolution method. * The result of parsing a serialized vibration. * * @see VibrationXmlParser * * @hide */ @TestApi @SuppressLint("UnflaggedApi") // @TestApi without associated feature. public class ParsedVibration { @TestApi // This was used in CTS before the flag was introduced. @SystemApi @FlaggedApi(FLAG_VIBRATION_XML_APIS) public final class ParsedVibration { private final List<VibrationEffect> mEffects; /** @hide */ @TestApi public ParsedVibration(@NonNull List<VibrationEffect> effects) { mEffects = effects; } Loading @@ -49,40 +53,28 @@ public class ParsedVibration { public ParsedVibration(@NonNull VibrationEffect effect) { mEffects = List.of(effect); } /** * Returns the first parsed vibration supported by {@code vibrator}, or {@code null} if none of * the parsed vibrations are supported. * * @hide */ @TestApi @TestApi // This was used in CTS before the flag was introduced. @SystemApi @FlaggedApi(FLAG_VIBRATION_XML_APIS) @Nullable public VibrationEffect resolve(@NonNull Vibrator vibrator) { return resolve(vibrator.getInfo()); } /** * Returns the parsed vibrations for testing purposes. * * <p>Real callers should not use this method. Instead, they should resolve to a * {@link VibrationEffect} via {@link #resolve(Vibrator)}. * * @hide */ @TestApi @VisibleForTesting @NonNull public List<VibrationEffect> getVibrationEffects() { return Collections.unmodifiableList(mEffects); } /** * Same as {@link #resolve(Vibrator)}, but uses {@link VibratorInfo} instead for resolving. * * @hide */ @Nullable public final VibrationEffect resolve(@NonNull VibratorInfo info) { public VibrationEffect resolve(@NonNull VibratorInfo info) { for (int i = 0; i < mEffects.size(); i++) { VibrationEffect effect = mEffects.get(i); if (info.areVibrationFeaturesSupported(effect)) { Loading @@ -91,4 +83,21 @@ public class ParsedVibration { } return null; } @Override public boolean equals(@Nullable Object o) { if (this == o) { return true; } if (!(o instanceof ParsedVibration)) { return false; } ParsedVibration other = (ParsedVibration) o; return mEffects.equals(other.mEffects); } @Override public int hashCode() { return Objects.hashCode(mEffects); } }
core/java/android/os/vibrator/persistence/VibrationXmlParser.java +123 −88 Original line number Diff line number Diff line Loading @@ -16,13 +16,15 @@ package android.os.vibrator.persistence; import static android.os.vibrator.Flags.FLAG_VIBRATION_XML_APIS; import android.annotation.FlaggedApi; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SuppressLint; import android.annotation.SystemApi; import android.annotation.TestApi; import android.os.VibrationEffect; import android.util.Slog; import android.util.Xml; import com.android.internal.vibrator.persistence.VibrationEffectXmlParser; Loading @@ -36,9 +38,12 @@ import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.Reader; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.List; Loading Loading @@ -116,10 +121,10 @@ import java.util.List; * * @hide */ @TestApi @SuppressLint("UnflaggedApi") // @TestApi without associated feature. @TestApi // This was used in CTS before the flag was introduced. @SystemApi @FlaggedApi(FLAG_VIBRATION_XML_APIS) public final class VibrationXmlParser { private static final String TAG = "VibrationXmlParser"; /** * The MIME type for a xml holding a vibration. Loading Loading @@ -168,93 +173,109 @@ public final class VibrationXmlParser { } /** * Parses XML content from given input stream into a {@link VibrationEffect}. * Parses XML content from given input stream into a {@link ParsedVibration}. * * <p>It supports both the "vibration-effect" and "vibration-select" root tags. * <ul> * <li>If "vibration-effect" is the root tag, the serialization provided should contain a * valid serialization for a single vibration. * <li>If "vibration-select" is the root tag, the serialization may contain one or more * valid vibration serializations. * </ul> * * <p>After parsing, it returns a {@link ParsedVibration} that opaquely represents the parsed * vibration(s), and the caller can get a concrete {@link VibrationEffect} by resolving this * result to a specific vibrator. * * <p>This parser fails with an exception if the content of the input stream does not follow the * schema or has unsupported values. * * @return a {@link ParsedVibration} * @throws IOException error reading from given {@link InputStream} or parsing the content. * * @hide */ @TestApi // Replacing test APIs used in CTS before the flagged system APIs was introduced. @SystemApi @FlaggedApi(FLAG_VIBRATION_XML_APIS) @NonNull public static ParsedVibration parse(@NonNull InputStream inputStream) throws IOException { return parseDocument(new InputStreamReader(inputStream, StandardCharsets.UTF_8)); } /** * Parses XML content from given input stream into a single {@link VibrationEffect}. * * <p>This method parses an XML content that contains a single, complete {@link VibrationEffect} * serialization. As such, the root tag must be a "vibration" tag. * serialization. As such, the root tag must be a "vibration-effect" tag. * * <p>This parser fails silently and returns {@code null} if the content of the input stream * does not follow the schema or has unsupported values. * <p>This parser fails with an exception if the content of the input stream does not follow the * schema or has unsupported values. * * @return the {@link VibrationEffect} if parsed successfully, {@code null} otherwise. * @throws IOException error reading from given {@link Reader} * @return the parsed {@link VibrationEffect} * @throws IOException error reading from given {@link InputStream} or parsing the content. * * @hide */ @TestApi @Nullable @TestApi // Replacing test APIs used in CTS before the flagged system APIs was introduced. @SystemApi @FlaggedApi(FLAG_VIBRATION_XML_APIS) @NonNull public static VibrationEffect parseVibrationEffect(@NonNull InputStream inputStream) throws IOException { return parseVibrationEffect(new InputStreamReader(inputStream, StandardCharsets.UTF_8)); } /** * Parses XML content from given {@link Reader} into a {@link VibrationEffect}. * * <p>Same as {@link #parseVibrationEffect(InputStream)}, but with a {@link Reader}. * * @hide */ @NonNull public static VibrationEffect parseVibrationEffect(@NonNull Reader reader) throws IOException { return parseVibrationEffect(reader, /* flags= */ 0); } /** * Parses XML content from given input stream into a {@link VibrationEffect}. * * <p>This method parses an XML content that contains a single, complete {@link VibrationEffect} * serialization. As such, the root tag must be a "vibration" tag. * Parses XML content from given {@link Reader} into a {@link VibrationEffect}. * * <p>Same as {@link #parseVibrationEffect(Reader)}, with extra flags to control the parsing * behavior. * * @hide */ @Nullable @NonNull public static VibrationEffect parseVibrationEffect(@NonNull Reader reader, @Flags int flags) throws IOException { try { return parseDocumentInternal( reader, flags, VibrationXmlParser::parseVibrationEffectInternal); } catch (XmlParserException | XmlPullParserException e) { Slog.w(TAG, "Error parsing vibration XML", e); return null; } return parseDocumentInternal(reader, flags, VibrationXmlParser::parseVibrationEffectInternal); } /** * Parses XML content from given input stream into a {@link ParsedVibration}. * Parses XML content from given {@link Reader} into a {@link ParsedVibration}. * * <p>It supports both the "vibration" and "vibration-select" root tags. * <ul> * <li>If "vibration" is the root tag, the serialization provided through {@code reader} * should contain a valid serialization for a single vibration. * <li>If "vibration-select" is the root tag, the serialization may contain one or more * valid vibration serializations. * </ul> * * <p>After parsing, it returns a {@link ParsedVibration} that opaquely represents the parsed * vibration(s), and the caller can get a concrete {@link VibrationEffect} by resolving this * result to a specific vibrator. * * <p>This parser fails silently and returns {@code null} if the content of the input does not * follow the schema or has unsupported values. * * @return a {@link ParsedVibration} * @throws IOException error reading from given {@link Reader} * <p>Same as {@link #parse(InputStream)}, but with a {@link Reader}. * * @hide */ @TestApi @Nullable @NonNull public static ParsedVibration parseDocument(@NonNull Reader reader) throws IOException { return parseDocument(reader, /* flags= */ 0); } /** * Parses XML content from given input stream into a {@link ParsedVibration}. * Parses XML content from given {@link Reader} into a {@link ParsedVibration}. * * <p>Same as {@link #parseDocument(Reader)}, with extra flags to control the parsing behavior. * * @hide */ @Nullable @NonNull public static ParsedVibration parseDocument(@NonNull Reader reader, @Flags int flags) throws IOException { try { return parseDocumentInternal(reader, flags, VibrationXmlParser::parseElementInternal); } catch (XmlParserException | XmlPullParserException e) { Slog.w(TAG, "Error parsing vibration/vibration-select XML", e); return null; } } /** Loading @@ -262,7 +283,7 @@ public final class VibrationXmlParser { * {@link ParsedVibration}. * * <p>Same as {@link #parseDocument(Reader, int)}, but, instead of parsing the full XML content, * it takes a parser that points to either a <vibration-effect> or a <vibration-select> start * it takes a parser that points to either a "vibration-effect" or a "vibration-select" start * tag. No other parser position, including start of document, is considered valid. * * <p>This method parses until an end "vibration-effect" or "vibration-select" tag (depending Loading @@ -270,37 +291,22 @@ public final class VibrationXmlParser { * will point to the end tag. * * @throws IOException error parsing from given {@link TypedXmlPullParser}. * @throws VibrationXmlParserException if the XML tag cannot be parsed into a * {@link ParsedVibration}. The given {@code parser} might be pointing to a child XML tag * that caused the parser failure. * The given {@code parser} might be pointing to a child XML tag that caused the parser * failure. * * @hide */ @NonNull public static ParsedVibration parseElement(@NonNull TypedXmlPullParser parser, @Flags int flags) throws IOException, VibrationXmlParserException { throws IOException { try { return parseElementInternal(parser, flags); } catch (XmlParserException e) { throw new VibrationXmlParserException("Error parsing vibration-select.", e); } } /** * Represents an error while parsing a vibration XML input. * * @hide */ public static final class VibrationXmlParserException extends Exception { private VibrationXmlParserException(String message, Throwable cause) { super(message, cause); } private VibrationXmlParserException(String message) { super(message); throw new ParseFailedException(e); } } @NonNull private static ParsedVibration parseElementInternal( @NonNull TypedXmlPullParser parser, @Flags int flags) throws IOException, XmlParserException { Loading @@ -313,11 +319,12 @@ public final class VibrationXmlParser { case XmlConstants.TAG_VIBRATION_SELECT: return parseVibrationSelectInternal(parser, flags); default: throw new XmlParserException( "Unexpected tag name when parsing element: " + tagName); throw new ParseFailedException( "Unexpected tag " + tagName + " when parsing a vibration"); } } @NonNull private static ParsedVibration parseVibrationSelectInternal( @NonNull TypedXmlPullParser parser, @Flags int flags) throws IOException, XmlParserException { Loading @@ -332,7 +339,7 @@ public final class VibrationXmlParser { return new ParsedVibration(effects); } /** Parses a single XML element for "vibration" tag into a {@link VibrationEffect}. */ @NonNull private static VibrationEffect parseVibrationEffectInternal( @NonNull TypedXmlPullParser parser, @Flags int flags) throws IOException, XmlParserException { Loading @@ -347,9 +354,11 @@ public final class VibrationXmlParser { * This method parses a whole XML document (provided through a {@link Reader}). The root tag is * parsed as per a provided {@link ElementParser}. */ @NonNull private static <T> T parseDocumentInternal( @NonNull Reader reader, @Flags int flags, ElementParser<T> parseLogic) throws IOException, XmlParserException, XmlPullParserException { throws IOException { try { TypedXmlPullParser parser = Xml.newFastPullParser(); parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true); parser.setInput(reader); Loading @@ -364,15 +373,41 @@ public final class VibrationXmlParser { XmlReader.readDocumentEndTag(parser); return result; } catch (XmlPullParserException e) { throw new ParseFailedException("Error initializing XMLPullParser", e); } catch (XmlParserException e) { throw new ParseFailedException(e); } } /** Encapsulate a logic to parse an XML element from an open parser. */ private interface ElementParser<T> { /** Parses a single XML element starting from the current position of the {@code parser}. */ @NonNull T parse(@NonNull TypedXmlPullParser parser, @Flags int flags) throws IOException, XmlParserException; } /** * Represents an error while parsing a vibration XML input. * * @hide */ @TestApi public static final class ParseFailedException extends IOException { private ParseFailedException(String message) { super(message); } private ParseFailedException(XmlParserException parserException) { this(parserException.getMessage(), parserException); } private ParseFailedException(String message, Throwable cause) { super(message, cause); } } private VibrationXmlParser() { } }