diff --git a/library/common/src/main/java/com/google/android/exoplayer2/metadata/MetadataDecoder.java b/library/common/src/main/java/com/google/android/exoplayer2/metadata/MetadataDecoder.java index d735c9d125..dee0db5a8e 100644 --- a/library/common/src/main/java/com/google/android/exoplayer2/metadata/MetadataDecoder.java +++ b/library/common/src/main/java/com/google/android/exoplayer2/metadata/MetadataDecoder.java @@ -27,7 +27,8 @@ public interface MetadataDecoder { * Decodes a {@link Metadata} element from the provided input buffer. * *
Respects {@link ByteBuffer#limit()} of {@code inputBuffer.data}, but assumes {@link - * ByteBuffer#position()} and {@link ByteBuffer#arrayOffset()} are both zero. + * ByteBuffer#position()} and {@link ByteBuffer#arrayOffset()} are both zero and {@link + * ByteBuffer#hasArray()} is true. * * @param inputBuffer The input buffer to decode. * @return The decoded metadata object, or null if the metadata could not be decoded. diff --git a/library/common/src/main/java/com/google/android/exoplayer2/metadata/emsg/EventMessageDecoder.java b/library/common/src/main/java/com/google/android/exoplayer2/metadata/emsg/EventMessageDecoder.java index d87376feb0..c03a5cb038 100644 --- a/library/common/src/main/java/com/google/android/exoplayer2/metadata/emsg/EventMessageDecoder.java +++ b/library/common/src/main/java/com/google/android/exoplayer2/metadata/emsg/EventMessageDecoder.java @@ -26,13 +26,12 @@ import java.util.Arrays; /** Decodes data encoded by {@link EventMessageEncoder}. */ public final class EventMessageDecoder implements MetadataDecoder { - @SuppressWarnings("ByteBufferBackingArray") @Override public Metadata decode(MetadataInputBuffer inputBuffer) { ByteBuffer buffer = Assertions.checkNotNull(inputBuffer.data); - byte[] data = buffer.array(); - int size = buffer.limit(); - return new Metadata(decode(new ParsableByteArray(data, size))); + Assertions.checkArgument( + buffer.position() == 0 && buffer.hasArray() && buffer.arrayOffset() == 0); + return new Metadata(decode(new ParsableByteArray(buffer.array(), buffer.limit()))); } public EventMessage decode(ParsableByteArray emsgData) { diff --git a/library/common/src/main/java/com/google/android/exoplayer2/metadata/id3/Id3Decoder.java b/library/common/src/main/java/com/google/android/exoplayer2/metadata/id3/Id3Decoder.java index faab7f0775..84a316e848 100644 --- a/library/common/src/main/java/com/google/android/exoplayer2/metadata/id3/Id3Decoder.java +++ b/library/common/src/main/java/com/google/android/exoplayer2/metadata/id3/Id3Decoder.java @@ -96,11 +96,12 @@ public final class Id3Decoder implements MetadataDecoder { this.framePredicate = framePredicate; } - @SuppressWarnings("ByteBufferBackingArray") @Override @Nullable public Metadata decode(MetadataInputBuffer inputBuffer) { ByteBuffer buffer = Assertions.checkNotNull(inputBuffer.data); + Assertions.checkArgument( + buffer.position() == 0 && buffer.hasArray() && buffer.arrayOffset() == 0); return decode(buffer.array(), buffer.limit()); } diff --git a/library/common/src/test/java/com/google/android/exoplayer2/metadata/emsg/EventMessageDecoderTest.java b/library/common/src/test/java/com/google/android/exoplayer2/metadata/emsg/EventMessageDecoderTest.java index 059463a1e6..b8ef4f42e5 100644 --- a/library/common/src/test/java/com/google/android/exoplayer2/metadata/emsg/EventMessageDecoderTest.java +++ b/library/common/src/test/java/com/google/android/exoplayer2/metadata/emsg/EventMessageDecoderTest.java @@ -16,13 +16,14 @@ package com.google.android.exoplayer2.metadata.emsg; import static com.google.android.exoplayer2.testutil.TestUtil.createByteArray; +import static com.google.android.exoplayer2.testutil.TestUtil.createMetadataInputBuffer; import static com.google.android.exoplayer2.testutil.TestUtil.joinByteArrays; import static com.google.common.truth.Truth.assertThat; +import static org.junit.Assert.assertThrows; import androidx.test.ext.junit.runners.AndroidJUnit4; import com.google.android.exoplayer2.metadata.Metadata; import com.google.android.exoplayer2.metadata.MetadataInputBuffer; -import java.nio.ByteBuffer; import org.junit.Test; import org.junit.runner.RunWith; @@ -40,10 +41,8 @@ public final class EventMessageDecoderTest { createByteArray(0, 15, 67, 211), // id = 1000403 createByteArray(0, 1, 2, 3, 4)); // message_data = {0, 1, 2, 3, 4} EventMessageDecoder decoder = new EventMessageDecoder(); - MetadataInputBuffer buffer = new MetadataInputBuffer(); - buffer.data = ByteBuffer.allocate(rawEmsgBody.length).put(rawEmsgBody); - Metadata metadata = decoder.decode(buffer); + Metadata metadata = decoder.decode(createMetadataInputBuffer(rawEmsgBody)); assertThat(metadata.length()).isEqualTo(1); EventMessage eventMessage = (EventMessage) metadata.get(0); @@ -54,4 +53,31 @@ public final class EventMessageDecoderTest { assertThat(eventMessage.messageData).isEqualTo(new byte[]{0, 1, 2, 3, 4}); } + @Test + public void testDecodeEventMessage_failsIfPositionNonZero() { + EventMessageDecoder decoder = new EventMessageDecoder(); + MetadataInputBuffer buffer = createMetadataInputBuffer(createByteArray(1, 2, 3)); + buffer.data.position(1); + + assertThrows(IllegalArgumentException.class, () -> decoder.decode(buffer)); + } + + @Test + public void testDecodeEventMessage_failsIfBufferHasNoArray() { + EventMessageDecoder decoder = new EventMessageDecoder(); + MetadataInputBuffer buffer = createMetadataInputBuffer(createByteArray(1, 2, 3)); + buffer.data = buffer.data.asReadOnlyBuffer(); + + assertThrows(IllegalArgumentException.class, () -> decoder.decode(buffer)); + } + + @Test + public void testDecodeEventMessage_failsIfArrayOffsetNonZero() { + EventMessageDecoder decoder = new EventMessageDecoder(); + MetadataInputBuffer buffer = createMetadataInputBuffer(createByteArray(1, 2, 3)); + buffer.data.position(1); + buffer.data = buffer.data.slice(); + + assertThrows(IllegalArgumentException.class, () -> decoder.decode(buffer)); + } } diff --git a/library/common/src/test/java/com/google/android/exoplayer2/metadata/emsg/EventMessageEncoderTest.java b/library/common/src/test/java/com/google/android/exoplayer2/metadata/emsg/EventMessageEncoderTest.java index 5ff9d08b9b..fc73b0cdaf 100644 --- a/library/common/src/test/java/com/google/android/exoplayer2/metadata/emsg/EventMessageEncoderTest.java +++ b/library/common/src/test/java/com/google/android/exoplayer2/metadata/emsg/EventMessageEncoderTest.java @@ -16,6 +16,7 @@ package com.google.android.exoplayer2.metadata.emsg; import static com.google.android.exoplayer2.testutil.TestUtil.createByteArray; +import static com.google.android.exoplayer2.testutil.TestUtil.createMetadataInputBuffer; import static com.google.android.exoplayer2.testutil.TestUtil.joinByteArrays; import static com.google.common.truth.Truth.assertThat; @@ -23,7 +24,6 @@ import androidx.test.ext.junit.runners.AndroidJUnit4; import com.google.android.exoplayer2.metadata.Metadata; import com.google.android.exoplayer2.metadata.MetadataInputBuffer; import java.io.IOException; -import java.nio.ByteBuffer; import org.junit.Test; import org.junit.runner.RunWith; @@ -51,8 +51,7 @@ public final class EventMessageEncoderTest { @Test public void encodeDecodeEventStream() throws IOException { byte[] encodedByteArray = new EventMessageEncoder().encode(DECODED_MESSAGE); - MetadataInputBuffer buffer = new MetadataInputBuffer(); - buffer.data = ByteBuffer.allocate(encodedByteArray.length).put(encodedByteArray); + MetadataInputBuffer buffer = createMetadataInputBuffer(encodedByteArray); EventMessageDecoder decoder = new EventMessageDecoder(); Metadata metadata = decoder.decode(buffer); diff --git a/library/common/src/test/java/com/google/android/exoplayer2/metadata/id3/Id3DecoderTest.java b/library/common/src/test/java/com/google/android/exoplayer2/metadata/id3/Id3DecoderTest.java index a753ad3110..6389417464 100644 --- a/library/common/src/test/java/com/google/android/exoplayer2/metadata/id3/Id3DecoderTest.java +++ b/library/common/src/test/java/com/google/android/exoplayer2/metadata/id3/Id3DecoderTest.java @@ -15,11 +15,15 @@ */ package com.google.android.exoplayer2.metadata.id3; +import static com.google.android.exoplayer2.testutil.TestUtil.createByteArray; +import static com.google.android.exoplayer2.testutil.TestUtil.createMetadataInputBuffer; import static com.google.common.truth.Truth.assertThat; +import static org.junit.Assert.assertThrows; import androidx.test.ext.junit.runners.AndroidJUnit4; import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.metadata.Metadata; +import com.google.android.exoplayer2.metadata.MetadataInputBuffer; import com.google.android.exoplayer2.util.Assertions; import java.nio.charset.Charset; import java.util.Arrays; @@ -233,6 +237,34 @@ public final class Id3DecoderTest { assertThat(apicFrame.pictureData).isEqualTo(new byte[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 0}); } + @Test + public void decodeFailsIfPositionNonZero() { + Id3Decoder decoder = new Id3Decoder(); + MetadataInputBuffer buffer = createMetadataInputBuffer(createByteArray(1, 2, 3)); + buffer.data.position(1); + + assertThrows(IllegalArgumentException.class, () -> decoder.decode(buffer)); + } + + @Test + public void decodeFailsIfBufferHasNoArray() { + Id3Decoder decoder = new Id3Decoder(); + MetadataInputBuffer buffer = createMetadataInputBuffer(createByteArray(1, 2, 3)); + buffer.data = buffer.data.asReadOnlyBuffer(); + + assertThrows(IllegalArgumentException.class, () -> decoder.decode(buffer)); + } + + @Test + public void decodeFailsIfArrayOffsetNonZero() { + Id3Decoder decoder = new Id3Decoder(); + MetadataInputBuffer buffer = createMetadataInputBuffer(createByteArray(1, 2, 3)); + buffer.data.position(1); + buffer.data = buffer.data.slice(); + + assertThrows(IllegalArgumentException.class, () -> decoder.decode(buffer)); + } + public static byte[] buildSingleFrameTag(String frameId, byte[] frameData) { return buildMultiFramesTag(new FrameSpec(frameId, frameData)); } diff --git a/library/core/src/main/java/com/google/android/exoplayer2/metadata/dvbsi/AppInfoTableDecoder.java b/library/core/src/main/java/com/google/android/exoplayer2/metadata/dvbsi/AppInfoTableDecoder.java index 10325b34ec..f533b97d13 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/metadata/dvbsi/AppInfoTableDecoder.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/metadata/dvbsi/AppInfoTableDecoder.java @@ -48,9 +48,10 @@ public final class AppInfoTableDecoder implements MetadataDecoder { @Override @Nullable - @SuppressWarnings("ByteBufferBackingArray") public Metadata decode(MetadataInputBuffer inputBuffer) { ByteBuffer buffer = Assertions.checkNotNull(inputBuffer.data); + Assertions.checkArgument( + buffer.position() == 0 && buffer.hasArray() && buffer.arrayOffset() == 0); int tableId = buffer.get(); return tableId == APPLICATION_INFORMATION_TABLE_ID ? parseAit(new ParsableBitArray(buffer.array(), buffer.limit())) diff --git a/library/core/src/main/java/com/google/android/exoplayer2/metadata/icy/IcyDecoder.java b/library/core/src/main/java/com/google/android/exoplayer2/metadata/icy/IcyDecoder.java index 854a8fc3a4..30b6ac1691 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/metadata/icy/IcyDecoder.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/metadata/icy/IcyDecoder.java @@ -45,9 +45,10 @@ public final class IcyDecoder implements MetadataDecoder { } @Override - @SuppressWarnings("ByteBufferBackingArray") public Metadata decode(MetadataInputBuffer inputBuffer) { ByteBuffer buffer = Assertions.checkNotNull(inputBuffer.data); + Assertions.checkArgument( + buffer.position() == 0 && buffer.hasArray() && buffer.arrayOffset() == 0); @Nullable String icyString = decodeToString(buffer); byte[] icyBytes = new byte[buffer.limit()]; buffer.get(icyBytes); diff --git a/library/core/src/main/java/com/google/android/exoplayer2/metadata/scte35/SpliceInfoDecoder.java b/library/core/src/main/java/com/google/android/exoplayer2/metadata/scte35/SpliceInfoDecoder.java index 2ac325a1a5..647e1296a9 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/metadata/scte35/SpliceInfoDecoder.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/metadata/scte35/SpliceInfoDecoder.java @@ -47,10 +47,11 @@ public final class SpliceInfoDecoder implements MetadataDecoder { sectionHeader = new ParsableBitArray(); } - @SuppressWarnings("ByteBufferBackingArray") @Override public Metadata decode(MetadataInputBuffer inputBuffer) { ByteBuffer buffer = Assertions.checkNotNull(inputBuffer.data); + Assertions.checkArgument( + buffer.position() == 0 && buffer.hasArray() && buffer.arrayOffset() == 0); // Internal timestamps adjustment. if (timestampAdjuster == null diff --git a/library/core/src/test/java/com/google/android/exoplayer2/metadata/dvbsi/AppInfoTableDecoderTest.java b/library/core/src/test/java/com/google/android/exoplayer2/metadata/dvbsi/AppInfoTableDecoderTest.java index 2befb4ef8b..39de14b893 100644 --- a/library/core/src/test/java/com/google/android/exoplayer2/metadata/dvbsi/AppInfoTableDecoderTest.java +++ b/library/core/src/test/java/com/google/android/exoplayer2/metadata/dvbsi/AppInfoTableDecoderTest.java @@ -15,7 +15,10 @@ */ package com.google.android.exoplayer2.metadata.dvbsi; +import static com.google.android.exoplayer2.testutil.TestUtil.createByteArray; +import static com.google.android.exoplayer2.testutil.TestUtil.createMetadataInputBuffer; import static com.google.common.truth.Truth.assertThat; +import static org.junit.Assert.assertThrows; import androidx.test.core.app.ApplicationProvider; import androidx.test.ext.junit.runners.AndroidJUnit4; @@ -23,7 +26,6 @@ import com.google.android.exoplayer2.metadata.Metadata; import com.google.android.exoplayer2.metadata.MetadataInputBuffer; import com.google.android.exoplayer2.testutil.TestUtil; import java.io.IOException; -import java.nio.ByteBuffer; import org.junit.Test; import org.junit.runner.RunWith; @@ -69,12 +71,32 @@ public final class AppInfoTableDecoderTest { assertThat(metadata).isNull(); } - private static MetadataInputBuffer createMetadataInputBuffer(byte[] data) { - MetadataInputBuffer inputBuffer = new MetadataInputBuffer(); - inputBuffer.data = ByteBuffer.allocate(data.length); - inputBuffer.data.put(data); - inputBuffer.data.flip(); - return inputBuffer; + @Test + public void decode_failsIfPositionNonZero() { + AppInfoTableDecoder decoder = new AppInfoTableDecoder(); + MetadataInputBuffer buffer = createMetadataInputBuffer(createByteArray(1, 2, 3)); + buffer.data.position(1); + + assertThrows(IllegalArgumentException.class, () -> decoder.decode(buffer)); + } + + @Test + public void decode_failsIfBufferHasNoArray() { + AppInfoTableDecoder decoder = new AppInfoTableDecoder(); + MetadataInputBuffer buffer = createMetadataInputBuffer(createByteArray(1, 2, 3)); + buffer.data = buffer.data.asReadOnlyBuffer(); + + assertThrows(IllegalArgumentException.class, () -> decoder.decode(buffer)); + } + + @Test + public void decode_failsIfArrayOffsetNonZero() { + AppInfoTableDecoder decoder = new AppInfoTableDecoder(); + MetadataInputBuffer buffer = createMetadataInputBuffer(createByteArray(1, 2, 3)); + buffer.data.position(1); + buffer.data = buffer.data.slice(); + + assertThrows(IllegalArgumentException.class, () -> decoder.decode(buffer)); } private static byte[] readTestFile(String name) throws IOException { diff --git a/library/core/src/test/java/com/google/android/exoplayer2/metadata/icy/IcyDecoderTest.java b/library/core/src/test/java/com/google/android/exoplayer2/metadata/icy/IcyDecoderTest.java index fcb958e7a6..49cca0367d 100644 --- a/library/core/src/test/java/com/google/android/exoplayer2/metadata/icy/IcyDecoderTest.java +++ b/library/core/src/test/java/com/google/android/exoplayer2/metadata/icy/IcyDecoderTest.java @@ -15,16 +15,18 @@ */ package com.google.android.exoplayer2.metadata.icy; +import static com.google.android.exoplayer2.testutil.TestUtil.createByteArray; +import static com.google.android.exoplayer2.testutil.TestUtil.createMetadataInputBuffer; import static com.google.common.truth.Truth.assertThat; import static java.nio.charset.StandardCharsets.ISO_8859_1; import static java.nio.charset.StandardCharsets.UTF_16; import static java.nio.charset.StandardCharsets.UTF_8; +import static org.junit.Assert.assertThrows; import androidx.test.ext.junit.runners.AndroidJUnit4; import com.google.android.exoplayer2.metadata.Metadata; import com.google.android.exoplayer2.metadata.MetadataInputBuffer; import com.google.android.exoplayer2.testutil.TestUtil; -import java.nio.ByteBuffer; import org.junit.Test; import org.junit.runner.RunWith; @@ -186,10 +188,28 @@ public final class IcyDecoderTest { assertThat(streamInfo.url).isNull(); } - private static MetadataInputBuffer createMetadataInputBuffer(byte[] data) { - MetadataInputBuffer metadataInputBuffer = new MetadataInputBuffer(); - metadataInputBuffer.data = ByteBuffer.allocate(data.length).put(data); - metadataInputBuffer.data.flip(); - return metadataInputBuffer; + @Test + public void decode_failsIfPositionNonZero() { + MetadataInputBuffer buffer = createMetadataInputBuffer(createByteArray(1, 2, 3)); + buffer.data.position(1); + + assertThrows(IllegalArgumentException.class, () -> decoder.decode(buffer)); + } + + @Test + public void decode_failsIfBufferHasNoArray() { + MetadataInputBuffer buffer = createMetadataInputBuffer(createByteArray(1, 2, 3)); + buffer.data = buffer.data.asReadOnlyBuffer(); + + assertThrows(IllegalArgumentException.class, () -> decoder.decode(buffer)); + } + + @Test + public void decode_failsIfArrayOffsetNonZero() { + MetadataInputBuffer buffer = createMetadataInputBuffer(createByteArray(1, 2, 3)); + buffer.data.position(1); + buffer.data = buffer.data.slice(); + + assertThrows(IllegalArgumentException.class, () -> decoder.decode(buffer)); } } diff --git a/library/core/src/test/java/com/google/android/exoplayer2/metadata/scte35/SpliceInfoDecoderTest.java b/library/core/src/test/java/com/google/android/exoplayer2/metadata/scte35/SpliceInfoDecoderTest.java index ff4a46a20f..90c2e7d386 100644 --- a/library/core/src/test/java/com/google/android/exoplayer2/metadata/scte35/SpliceInfoDecoderTest.java +++ b/library/core/src/test/java/com/google/android/exoplayer2/metadata/scte35/SpliceInfoDecoderTest.java @@ -16,7 +16,10 @@ package com.google.android.exoplayer2.metadata.scte35; import static com.google.android.exoplayer2.C.TIME_UNSET; +import static com.google.android.exoplayer2.testutil.TestUtil.createByteArray; +import static com.google.android.exoplayer2.testutil.TestUtil.createMetadataInputBuffer; import static com.google.common.truth.Truth.assertThat; +import static org.junit.Assert.assertThrows; import androidx.test.ext.junit.runners.AndroidJUnit4; import com.google.android.exoplayer2.metadata.Metadata; @@ -162,9 +165,35 @@ public final class SpliceInfoDecoderTest { assertThat(command.availsExpected).isEqualTo(2); } + @Test + public void decodeFailsIfPositionNonZero() { + MetadataInputBuffer buffer = createMetadataInputBuffer(createByteArray(1, 2, 3)); + buffer.data.position(1); + + assertThrows(IllegalArgumentException.class, () -> decoder.decode(buffer)); + } + + @Test + public void decodeFailsIfBufferHasNoArray() { + MetadataInputBuffer buffer = createMetadataInputBuffer(createByteArray(1, 2, 3)); + buffer.data = buffer.data.asReadOnlyBuffer(); + + assertThrows(IllegalArgumentException.class, () -> decoder.decode(buffer)); + } + + @Test + public void decodeFailsIfArrayOffsetNonZero() { + MetadataInputBuffer buffer = createMetadataInputBuffer(createByteArray(1, 2, 3)); + buffer.data.position(1); + buffer.data = buffer.data.slice(); + + assertThrows(IllegalArgumentException.class, () -> decoder.decode(buffer)); + } + private Metadata feedInputBuffer(byte[] data, long timeUs, long subsampleOffset) { inputBuffer.clear(); inputBuffer.data = ByteBuffer.allocate(data.length).put(data); + inputBuffer.data.flip(); inputBuffer.timeUs = timeUs; inputBuffer.subsampleOffsetUs = subsampleOffset; return decoder.decode(inputBuffer); diff --git a/testutils/src/main/java/com/google/android/exoplayer2/testutil/TestUtil.java b/testutils/src/main/java/com/google/android/exoplayer2/testutil/TestUtil.java index 87819e212e..7b45274177 100644 --- a/testutils/src/main/java/com/google/android/exoplayer2/testutil/TestUtil.java +++ b/testutils/src/main/java/com/google/android/exoplayer2/testutil/TestUtil.java @@ -34,6 +34,7 @@ import com.google.android.exoplayer2.extractor.Extractor; import com.google.android.exoplayer2.extractor.ExtractorInput; import com.google.android.exoplayer2.extractor.PositionHolder; import com.google.android.exoplayer2.extractor.SeekMap; +import com.google.android.exoplayer2.metadata.MetadataInputBuffer; import com.google.android.exoplayer2.upstream.DataSource; import com.google.android.exoplayer2.upstream.DataSpec; import com.google.android.exoplayer2.util.Assertions; @@ -42,6 +43,7 @@ import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; +import java.nio.ByteBuffer; import java.util.Arrays; import java.util.Random; @@ -473,4 +475,15 @@ public class TestUtil { } return new DefaultExtractorInput(dataSource, position, length); } + + /** + * Create a new {@link MetadataInputBuffer} and copy {@code data} into the backing {@link + * ByteBuffer}. + */ + public static MetadataInputBuffer createMetadataInputBuffer(byte[] data) { + MetadataInputBuffer buffer = new MetadataInputBuffer(); + buffer.data = ByteBuffer.allocate(data.length).put(data); + buffer.data.flip(); + return buffer; + } }