diff --git a/core/api/current.txt b/core/api/current.txt index 4de91bbd81c18..f041276788adf 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -24175,6 +24175,7 @@ package android.location { package android.media { public final class ApplicationMediaCapabilities implements android.os.Parcelable { + method @NonNull public static android.media.ApplicationMediaCapabilities createFromXml(@NonNull org.xmlpull.v1.XmlPullParser); method public int describeContents(); method @NonNull public java.util.List getSupportedHdrTypes(); method @NonNull public java.util.List getSupportedVideoMimeTypes(); diff --git a/media/java/android/media/ApplicationMediaCapabilities.java b/media/java/android/media/ApplicationMediaCapabilities.java index 792e85f5d8396..36f6b94b16ade 100644 --- a/media/java/android/media/ApplicationMediaCapabilities.java +++ b/media/java/android/media/ApplicationMediaCapabilities.java @@ -22,10 +22,16 @@ import android.net.Uri; import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; +import android.util.Log; + +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; import java.util.ArrayList; +import java.util.HashMap; import java.util.HashSet; import java.util.List; +import java.util.Map; import java.util.Set; /** @@ -179,6 +185,20 @@ public final class ApplicationMediaCapabilities implements Parcelable { return mIsSlowMotionSupported; } + /** + * Creates {@link ApplicationMediaCapabilities} from an xml. + * @param xmlParser The underlying {@link XmlPullParser} that will read the xml. + * @return An ApplicationMediaCapabilities object. + * @throws UnsupportedOperationException if the capabilities in xml config are invalid or + * incompatible. + */ + @NonNull + public static ApplicationMediaCapabilities createFromXml(@NonNull XmlPullParser xmlParser) { + ApplicationMediaCapabilities.Builder builder = new ApplicationMediaCapabilities.Builder(); + builder.parseXml(xmlParser); + return builder.build(); + } + /** * Builder class for {@link ApplicationMediaCapabilities} objects. * Use this class to configure and create an ApplicationMediaCapabilities instance. Builder @@ -195,12 +215,126 @@ public final class ApplicationMediaCapabilities implements Parcelable { private boolean mIsSlowMotionSupported = false; + /* Map to save the format read from the xml. */ + private Map mFormatSupportedMap = new HashMap(); + /** * Constructs a new Builder with all the supports default to false. */ public Builder() { } + private void parseXml(@NonNull XmlPullParser xmlParser) + throws UnsupportedOperationException { + if (xmlParser == null) { + throw new IllegalArgumentException("XmlParser must not be null"); + } + + try { + while (xmlParser.next() != XmlPullParser.START_TAG) { + continue; + } + + // Validates the tag is "media-capabilities". + if (!xmlParser.getName().equals("media-capabilities")) { + throw new UnsupportedOperationException("Invalid tag"); + } + + xmlParser.next(); + while (xmlParser.getEventType() != XmlPullParser.END_TAG) { + while (xmlParser.getEventType() != XmlPullParser.START_TAG) { + if (xmlParser.getEventType() == XmlPullParser.END_DOCUMENT) { + return; + } + xmlParser.next(); + } + + // Validates the tag is "format". + if (xmlParser.getName().equals("format")) { + parseFormatTag(xmlParser); + } else { + throw new UnsupportedOperationException("Invalid tag"); + } + while (xmlParser.getEventType() != XmlPullParser.END_TAG) { + xmlParser.next(); + } + xmlParser.next(); + } + } catch (XmlPullParserException xppe) { + throw new UnsupportedOperationException("Ill-formatted xml file"); + } catch (java.io.IOException ioe) { + throw new UnsupportedOperationException("Unable to read xml file"); + } + } + + private void parseFormatTag(XmlPullParser xmlParser) { + String name = null; + String supported = null; + for (int i = 0; i < xmlParser.getAttributeCount(); i++) { + String attrName = xmlParser.getAttributeName(i); + if (attrName.equals("name")) { + name = xmlParser.getAttributeValue(i); + } else if (attrName.equals("supported")) { + supported = xmlParser.getAttributeValue(i); + } else { + throw new UnsupportedOperationException("Invalid attribute name " + attrName); + } + } + + if (name != null && supported != null) { + if (!supported.equals("true") && !supported.equals("false")) { + throw new UnsupportedOperationException( + ("Supported value must be either true or false")); + } + boolean isSupported = Boolean.parseBoolean(supported); + + // Check if the format is already found before. + if (mFormatSupportedMap.get(name) != null && mFormatSupportedMap.get(name) + != isSupported) { + throw new UnsupportedOperationException( + "Format: " + name + " has conflict supported value"); + } + + switch (name) { + case "HEVC": + if (isSupported) { + mSupportedVideoMimeTypes.add(MediaFormat.MIMETYPE_VIDEO_HEVC); + } + break; + case "HDR10": + if (isSupported) { + mSupportedHdrTypes.add(MediaFeature.HdrType.HDR10); + } + break; + case "HDR10Plus": + if (isSupported) { + mSupportedHdrTypes.add(MediaFeature.HdrType.HDR10_PLUS); + } + break; + case "Dolby-Vision": + if (isSupported) { + mSupportedHdrTypes.add(MediaFeature.HdrType.DOLBY_VISION); + } + break; + case "HLG": + if (isSupported) { + mSupportedHdrTypes.add(MediaFeature.HdrType.HLG); + } + break; + case "SlowMotion": + mIsSlowMotionSupported = isSupported; + break; + default: + throw new UnsupportedOperationException("Invalid format name " + name); + } + // Save the name and isSupported into the map for validate later. + mFormatSupportedMap.put(name, isSupported); + } else { + throw new UnsupportedOperationException( + "Format name and supported must both be specified"); + } + } + /** * Builds a {@link ApplicationMediaCapabilities} object. * @@ -213,6 +347,11 @@ public final class ApplicationMediaCapabilities implements Parcelable { */ @NonNull public ApplicationMediaCapabilities build() { + Log.d(TAG, + "Building ApplicationMediaCapabilities with: " + mSupportedHdrTypes.toString() + + " " + mSupportedVideoMimeTypes.toString() + " " + + mIsSlowMotionSupported); + // If hdr is supported, application must also support hevc. if (!mSupportedHdrTypes.isEmpty() && !mSupportedVideoMimeTypes.contains( MediaFormat.MIMETYPE_VIDEO_HEVC)) { diff --git a/media/tests/MediaTranscodingTest/assets/ConflictSupportedValue.xml b/media/tests/MediaTranscodingTest/assets/ConflictSupportedValue.xml new file mode 100644 index 0000000000000..9b2fa3b3f28c0 --- /dev/null +++ b/media/tests/MediaTranscodingTest/assets/ConflictSupportedValue.xml @@ -0,0 +1,20 @@ + + + + + + + diff --git a/media/tests/MediaTranscodingTest/assets/EmptyFormat.xml b/media/tests/MediaTranscodingTest/assets/EmptyFormat.xml new file mode 100644 index 0000000000000..5ef5e51e2e74c --- /dev/null +++ b/media/tests/MediaTranscodingTest/assets/EmptyFormat.xml @@ -0,0 +1,19 @@ + + + + + + diff --git a/media/tests/MediaTranscodingTest/assets/FormatWithoutSupported.xml b/media/tests/MediaTranscodingTest/assets/FormatWithoutSupported.xml new file mode 100644 index 0000000000000..e50c212999841 --- /dev/null +++ b/media/tests/MediaTranscodingTest/assets/FormatWithoutSupported.xml @@ -0,0 +1,19 @@ + + + + + + diff --git a/media/tests/MediaTranscodingTest/assets/MediaCapabilities.xml b/media/tests/MediaTranscodingTest/assets/MediaCapabilities.xml new file mode 100644 index 0000000000000..3bff61e979e2c --- /dev/null +++ b/media/tests/MediaTranscodingTest/assets/MediaCapabilities.xml @@ -0,0 +1,21 @@ + + + + + + + + diff --git a/media/tests/MediaTranscodingTest/assets/SupportAllHdr.xml b/media/tests/MediaTranscodingTest/assets/SupportAllHdr.xml new file mode 100644 index 0000000000000..5cf66b0bff86a --- /dev/null +++ b/media/tests/MediaTranscodingTest/assets/SupportAllHdr.xml @@ -0,0 +1,24 @@ + + + + + + + + + + + diff --git a/media/tests/MediaTranscodingTest/assets/SupportHdrWithoutHevc.xml b/media/tests/MediaTranscodingTest/assets/SupportHdrWithoutHevc.xml new file mode 100644 index 0000000000000..309aa1d154a79 --- /dev/null +++ b/media/tests/MediaTranscodingTest/assets/SupportHdrWithoutHevc.xml @@ -0,0 +1,20 @@ + + + + + + + diff --git a/media/tests/MediaTranscodingTest/assets/SupportedWithoutFormat.xml b/media/tests/MediaTranscodingTest/assets/SupportedWithoutFormat.xml new file mode 100644 index 0000000000000..29454fcc5312d --- /dev/null +++ b/media/tests/MediaTranscodingTest/assets/SupportedWithoutFormat.xml @@ -0,0 +1,19 @@ + + + + + + diff --git a/media/tests/MediaTranscodingTest/assets/WrongBooleanValue.xml b/media/tests/MediaTranscodingTest/assets/WrongBooleanValue.xml new file mode 100644 index 0000000000000..62de6cd6e55a0 --- /dev/null +++ b/media/tests/MediaTranscodingTest/assets/WrongBooleanValue.xml @@ -0,0 +1,21 @@ + + + + + + + + diff --git a/media/tests/MediaTranscodingTest/assets/WrongMediaCapabilityTag.xml b/media/tests/MediaTranscodingTest/assets/WrongMediaCapabilityTag.xml new file mode 100644 index 0000000000000..5db42e5406373 --- /dev/null +++ b/media/tests/MediaTranscodingTest/assets/WrongMediaCapabilityTag.xml @@ -0,0 +1,21 @@ + + + + + + + + diff --git a/media/tests/MediaTranscodingTest/assets/WrongMediaCapabilityTag2.xml b/media/tests/MediaTranscodingTest/assets/WrongMediaCapabilityTag2.xml new file mode 100644 index 0000000000000..e924c48d1e1de --- /dev/null +++ b/media/tests/MediaTranscodingTest/assets/WrongMediaCapabilityTag2.xml @@ -0,0 +1,21 @@ + + + + + + + + diff --git a/media/tests/MediaTranscodingTest/res/xml/mediacapabilities.xml b/media/tests/MediaTranscodingTest/res/xml/mediacapabilities.xml new file mode 100644 index 0000000000000..3bff61e979e2c --- /dev/null +++ b/media/tests/MediaTranscodingTest/res/xml/mediacapabilities.xml @@ -0,0 +1,21 @@ + + + + + + + + diff --git a/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/ApplicationMediaCapabilitiesTest.java b/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/ApplicationMediaCapabilitiesTest.java index 3f3f49ae23181..74552a5f9c28f 100644 --- a/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/ApplicationMediaCapabilitiesTest.java +++ b/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/ApplicationMediaCapabilitiesTest.java @@ -26,28 +26,46 @@ package com.android.mediatranscodingtest; adb install -r testcases/mediatranscodingtest/arm64/mediatranscodingtest.apk adb shell am instrument -e class \ - com.android.mediatranscodingtest.MediaCapabilityTest \ + com.android.mediatranscodingtest.ApplicationMediaCapabilitiesTest \ -w com.android.mediatranscodingtest/.MediaTranscodingTestRunner * */ import static org.testng.Assert.assertThrows; +import android.content.Context; +import android.content.res.XmlResourceParser; import android.media.ApplicationMediaCapabilities; import android.media.MediaFeature; import android.media.MediaFormat; import android.test.ActivityInstrumentationTestCase2; +import android.util.Log; +import android.util.Xml; import org.junit.Test; +import org.xmlpull.v1.XmlPullParser; + +import java.io.InputStream; +import java.nio.charset.StandardCharsets; public class ApplicationMediaCapabilitiesTest extends ActivityInstrumentationTestCase2 { private static final String TAG = "MediaCapabilityTest"; + private Context mContext; + public ApplicationMediaCapabilitiesTest() { super("com.android.MediaCapabilityTest", MediaTranscodingTest.class); } + public void setUp() throws Exception { + Log.d(TAG, "setUp"); + super.setUp(); + + mContext = getInstrumentation().getContext(); + } + + @Test public void testSetSupportHevc() throws Exception { ApplicationMediaCapabilities capability = @@ -95,4 +113,194 @@ public class ApplicationMediaCapabilitiesTest extends MediaFeature.HdrType.HDR10_PLUS).build(); }); } + + // Test read the application's xml from res/xml folder using the XmlResourceParser. + // + // + // + @Test + public void testReadMediaCapabilitiesXml() throws Exception { + XmlResourceParser parser = mContext.getResources().getXml(R.xml.mediacapabilities); + ApplicationMediaCapabilities capability = ApplicationMediaCapabilities.createFromXml( + parser); + assertFalse(capability.isHdrTypeSupported(MediaFeature.HdrType.HDR10)); + assertFalse(capability.isSlowMotionSupported()); + assertTrue(capability.isVideoMimeTypeSupported(MediaFormat.MIMETYPE_VIDEO_HEVC)); + } + + // Test read the application's xml from res/xml folder using the XmlResourceParser. + // + // + // + // + // + // + @Test + public void testReadMediaCapabilitiesXmlWithSupportAllHdr() throws Exception { + InputStream xmlIs = mContext.getAssets().open("SupportAllHdr.xml"); + final XmlPullParser parser = Xml.newPullParser(); + parser.setInput(xmlIs, StandardCharsets.UTF_8.name()); + + ApplicationMediaCapabilities capability = ApplicationMediaCapabilities.createFromXml( + parser); + assertTrue(capability.isHdrTypeSupported(MediaFeature.HdrType.HDR10)); + assertTrue(capability.isHdrTypeSupported(MediaFeature.HdrType.HDR10_PLUS)); + assertTrue(capability.isHdrTypeSupported(MediaFeature.HdrType.DOLBY_VISION)); + assertTrue(capability.isHdrTypeSupported(MediaFeature.HdrType.HLG)); + assertTrue(capability.isSlowMotionSupported()); + assertTrue(capability.isVideoMimeTypeSupported(MediaFormat.MIMETYPE_VIDEO_HEVC)); + } + + // Test read the xml from res/assets folder using the InputStream. + // + // + // + @Test + public void testReadFromCorrectXmlWithInputStreamInAssets() throws Exception { + InputStream xmlIs = mContext.getAssets().open("MediaCapabilities.xml"); + final XmlPullParser parser = Xml.newPullParser(); + parser.setInput(xmlIs, StandardCharsets.UTF_8.name()); + + ApplicationMediaCapabilities capability = ApplicationMediaCapabilities.createFromXml( + parser); + assertFalse(capability.isHdrTypeSupported(MediaFeature.HdrType.HDR10)); + assertFalse(capability.isSlowMotionSupported()); + assertTrue(capability.isVideoMimeTypeSupported(MediaFormat.MIMETYPE_VIDEO_HEVC)); + } + + // Test parsing invalid xml with wrong tag expect UnsupportedOperationException + // MediaCapability does not match MediaCapabilities at the end which will lead to + // exception with "Ill-formatted xml file" + // + // + // + // + // + @Test + public void testReadFromWrongMediaCapabilityXml() throws Exception { + assertThrows(UnsupportedOperationException.class, () -> { + InputStream xmlIs = mContext.getAssets().open("WrongMediaCapabilityTag.xml"); + final XmlPullParser parser = Xml.newPullParser(); + parser.setInput(xmlIs, StandardCharsets.UTF_8.name()); + ApplicationMediaCapabilities capability = ApplicationMediaCapabilities.createFromXml( + parser); + }); + } + + // Test invalid xml with wrong tag expect UnsupportedOperationException + // MediaCapability is wrong tag. + // + // + // + // + // + @Test + public void testReadFromWrongMediaCapabilityXml2() throws Exception { + assertThrows(UnsupportedOperationException.class, () -> { + InputStream xmlIs = mContext.getAssets().open("WrongMediaCapabilityTag2.xml"); + final XmlPullParser parser = Xml.newPullParser(); + parser.setInput(xmlIs, StandardCharsets.UTF_8.name()); + ApplicationMediaCapabilities capability = ApplicationMediaCapabilities.createFromXml( + parser); + }); + } + + // Test invalid attribute value of "support" with true->yes expect UnsupportedOperationException + // + // + // + // + // + @Test + public void testReadFromXmlWithWrongBoolean() throws Exception { + assertThrows(UnsupportedOperationException.class, () -> { + InputStream xmlIs = mContext.getAssets().open("WrongBooleanValue.xml"); + final XmlPullParser parser = Xml.newPullParser(); + parser.setInput(xmlIs, StandardCharsets.UTF_8.name()); + ApplicationMediaCapabilities capability = ApplicationMediaCapabilities.createFromXml( + parser); + }); + } + + // Test parsing capabilities that support HDR10 but not support HEVC. + // Expect UnsupportedOperationException + // + // + // + // + @Test + public void testReadXmlSupportHdrWithoutSupportHevc() throws Exception { + assertThrows(UnsupportedOperationException.class, () -> { + InputStream xmlIs = mContext.getAssets().open("SupportHdrWithoutHevc.xml"); + final XmlPullParser parser = Xml.newPullParser(); + parser.setInput(xmlIs, StandardCharsets.UTF_8.name()); + ApplicationMediaCapabilities capability = ApplicationMediaCapabilities.createFromXml( + parser); + }); + } + + // Test parsing capabilities that has conflicted supported value. + // Expect UnsupportedOperationException + // + // + // + // + @Test + public void testReadXmlConflictSupportedValue() throws Exception { + assertThrows(UnsupportedOperationException.class, () -> { + InputStream xmlIs = mContext.getAssets().open("ConflictSupportedValue.xml"); + final XmlPullParser parser = Xml.newPullParser(); + parser.setInput(xmlIs, StandardCharsets.UTF_8.name()); + ApplicationMediaCapabilities capability = ApplicationMediaCapabilities.createFromXml( + parser); + }); + } + + // Test parsing capabilities that has empty format. + // Expect UnsupportedOperationException + // + // + // + @Test + public void testReadXmlWithEmptyFormat() throws Exception { + assertThrows(UnsupportedOperationException.class, () -> { + InputStream xmlIs = mContext.getAssets().open("EmptyFormat.xml"); + final XmlPullParser parser = Xml.newPullParser(); + parser.setInput(xmlIs, StandardCharsets.UTF_8.name()); + ApplicationMediaCapabilities capability = ApplicationMediaCapabilities.createFromXml( + parser); + }); + } + + // Test parsing capabilities that has empty format. + // Expect UnsupportedOperationException + // + // + // + @Test + public void testReadXmlFormatWithoutSupported() throws Exception { + assertThrows(UnsupportedOperationException.class, () -> { + InputStream xmlIs = mContext.getAssets().open("FormatWithoutSupported.xml"); + final XmlPullParser parser = Xml.newPullParser(); + parser.setInput(xmlIs, StandardCharsets.UTF_8.name()); + ApplicationMediaCapabilities capability = ApplicationMediaCapabilities.createFromXml( + parser); + }); + } + + // Test parsing capabilities that has supported without the format name. + // Expect UnsupportedOperationException + // + // + // + @Test + public void testReadXmlSupportedWithoutFormat() throws Exception { + assertThrows(UnsupportedOperationException.class, () -> { + InputStream xmlIs = mContext.getAssets().open("SupportedWithoutFormat.xml"); + final XmlPullParser parser = Xml.newPullParser(); + parser.setInput(xmlIs, StandardCharsets.UTF_8.name()); + ApplicationMediaCapabilities capability = ApplicationMediaCapabilities.createFromXml( + parser); + }); + } }