From ef8a47ba7db79c12fce7477dc40de50f62a9e6bb Mon Sep 17 00:00:00 2001 From: andrewlewis Date: Wed, 6 Jun 2018 08:55:07 -0700 Subject: [PATCH] Fix some categories of error prone warnings When switching from Stack to ArrayDeque, calls to add() need to be replaced by calls to push() because ArrayDeque treats the first element in the list as the top of the stack. String.split() has counterintuitive default behavior; see https://github.com/google/error-prone/blob/master/docs/bugpattern/StringSplitter.md. I've switched usages to pass limit = -1 argument, which means empty elements are no longer removed from the end of the returned array. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=199472592 --- .../exoplayer2/ExoPlayerImplInternal.java | 1 + .../exoplayer2/decoder/SimpleDecoder.java | 10 ++--- .../exoplayer2/drm/HttpMediaDrmCallback.java | 3 +- .../extractor/mkv/DefaultEbmlReader.java | 16 ++++--- .../exoplayer2/extractor/mkv/Sniffer.java | 5 ++- .../mp4/FixedSampleSizeRechunker.java | 3 ++ .../extractor/mp4/FragmentedMp4Extractor.java | 7 ++- .../extractor/mp4/Mp4Extractor.java | 8 ++-- .../extractor/mp4/PsshAtomUtil.java | 1 + .../exoplayer2/extractor/ogg/OpusReader.java | 2 +- .../exoplayer2/extractor/ogg/VorbisUtil.java | 10 +++-- .../extractor/wav/WavHeaderReader.java | 6 ++- .../offline/ProgressiveDownloader.java | 2 +- .../exoplayer2/offline/SegmentDownloader.java | 2 + .../android/exoplayer2/source/TrackGroup.java | 5 ++- .../exoplayer2/text/cea/Cea708Decoder.java | 8 ++-- .../exoplayer2/text/cea/CeaDecoder.java | 10 ++--- .../exoplayer2/text/ssa/SsaDecoder.java | 2 +- .../exoplayer2/text/ttml/TtmlDecoder.java | 19 ++++---- .../exoplayer2/text/tx3g/Tx3gDecoder.java | 3 +- .../exoplayer2/text/webvtt/CssParser.java | 3 +- .../text/webvtt/Mp4WebvttDecoder.java | 3 +- .../text/webvtt/WebvttCueParser.java | 9 ++-- .../text/webvtt/WebvttParserUtil.java | 5 ++- .../trackselection/BaseTrackSelection.java | 3 ++ .../trackselection/TrackSelection.java | 4 +- .../upstream/DataSchemeDataSource.java | 5 ++- .../cache/ContentMetadataInternal.java | 6 ++- .../upstream/crypto/AesFlushingCipher.java | 5 ++- .../android/exoplayer2/util/ColorParser.java | 5 ++- .../android/exoplayer2/util/MimeTypes.java | 4 +- .../exoplayer2/util/ParsableBitArray.java | 20 +++++---- .../exoplayer2/util/ParsableByteArray.java | 6 +-- .../util/ParsableNalUnitBitArray.java | 2 +- .../google/android/exoplayer2/util/Util.java | 43 ++++++++++++++++++- 35 files changed, 169 insertions(+), 77 deletions(-) diff --git a/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImplInternal.java b/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImplInternal.java index fc946804f4..a7d569081e 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImplInternal.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImplInternal.java @@ -1543,6 +1543,7 @@ import java.util.Collections; } } + @SuppressWarnings("ParameterNotNullable") private void updatePlayingPeriodRenderers(@Nullable MediaPeriodHolder oldPlayingPeriodHolder) throws ExoPlaybackException { MediaPeriodHolder newPlayingPeriodHolder = queue.getPlayingPeriod(); diff --git a/library/core/src/main/java/com/google/android/exoplayer2/decoder/SimpleDecoder.java b/library/core/src/main/java/com/google/android/exoplayer2/decoder/SimpleDecoder.java index 68089d7b41..441d3899a3 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/decoder/SimpleDecoder.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/decoder/SimpleDecoder.java @@ -17,7 +17,7 @@ package com.google.android.exoplayer2.decoder; import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.util.Assertions; -import java.util.LinkedList; +import java.util.ArrayDeque; /** * Base class for {@link Decoder}s that use their own decode thread. @@ -28,8 +28,8 @@ public abstract class SimpleDecoder queuedInputBuffers; - private final LinkedList queuedOutputBuffers; + private final ArrayDeque queuedInputBuffers; + private final ArrayDeque queuedOutputBuffers; private final I[] availableInputBuffers; private final O[] availableOutputBuffers; @@ -48,8 +48,8 @@ public abstract class SimpleDecoder(); - queuedOutputBuffers = new LinkedList<>(); + queuedInputBuffers = new ArrayDeque<>(); + queuedOutputBuffers = new ArrayDeque<>(); availableInputBuffers = inputBuffers; availableInputBufferCount = inputBuffers.length; for (int i = 0; i < availableInputBufferCount; i++) { diff --git a/library/core/src/main/java/com/google/android/exoplayer2/drm/HttpMediaDrmCallback.java b/library/core/src/main/java/com/google/android/exoplayer2/drm/HttpMediaDrmCallback.java index 4a93ac8333..9150a72b53 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/drm/HttpMediaDrmCallback.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/drm/HttpMediaDrmCallback.java @@ -108,7 +108,8 @@ public final class HttpMediaDrmCallback implements MediaDrmCallback { @Override public byte[] executeProvisionRequest(UUID uuid, ProvisionRequest request) throws IOException { - String url = request.getDefaultUrl() + "&signedRequest=" + new String(request.getData()); + String url = + request.getDefaultUrl() + "&signedRequest=" + Util.fromUtf8Bytes(request.getData()); return executePost(dataSourceFactory, url, new byte[0], null); } diff --git a/library/core/src/main/java/com/google/android/exoplayer2/extractor/mkv/DefaultEbmlReader.java b/library/core/src/main/java/com/google/android/exoplayer2/extractor/mkv/DefaultEbmlReader.java index 21cb3775e5..c0494e1ee0 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/extractor/mkv/DefaultEbmlReader.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/extractor/mkv/DefaultEbmlReader.java @@ -24,7 +24,7 @@ import java.io.EOFException; import java.io.IOException; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; -import java.util.Stack; +import java.util.ArrayDeque; /** * Default implementation of {@link EbmlReader}. @@ -46,15 +46,21 @@ import java.util.Stack; private static final int VALID_FLOAT32_ELEMENT_SIZE_BYTES = 4; private static final int VALID_FLOAT64_ELEMENT_SIZE_BYTES = 8; - private final byte[] scratch = new byte[8]; - private final Stack masterElementsStack = new Stack<>(); - private final VarintReader varintReader = new VarintReader(); + private final byte[] scratch; + private final ArrayDeque masterElementsStack; + private final VarintReader varintReader; private EbmlReaderOutput output; private @ElementState int elementState; private int elementId; private long elementContentSize; + public DefaultEbmlReader() { + scratch = new byte[8]; + masterElementsStack = new ArrayDeque<>(); + varintReader = new VarintReader(); + } + @Override public void init(EbmlReaderOutput eventHandler) { this.output = eventHandler; @@ -100,7 +106,7 @@ import java.util.Stack; case EbmlReaderOutput.TYPE_MASTER: long elementContentPosition = input.getPosition(); long elementEndPosition = elementContentPosition + elementContentSize; - masterElementsStack.add(new MasterElement(elementId, elementEndPosition)); + masterElementsStack.push(new MasterElement(elementId, elementEndPosition)); output.startMasterElement(elementId, elementContentPosition, elementContentSize); elementState = ELEMENT_STATE_READ_ID; return true; diff --git a/library/core/src/main/java/com/google/android/exoplayer2/extractor/mkv/Sniffer.java b/library/core/src/main/java/com/google/android/exoplayer2/extractor/mkv/Sniffer.java index a3fde6d455..62c9404916 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/extractor/mkv/Sniffer.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/extractor/mkv/Sniffer.java @@ -78,8 +78,9 @@ import java.io.IOException; return false; } if (size != 0) { - input.advancePeekPosition((int) size); - peekLength += size; + int sizeInt = (int) size; + input.advancePeekPosition(sizeInt); + peekLength += sizeInt; } } return peekLength == headerStart + headerSize; diff --git a/library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/FixedSampleSizeRechunker.java b/library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/FixedSampleSizeRechunker.java index 8336a280a2..536f70048c 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/FixedSampleSizeRechunker.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/FixedSampleSizeRechunker.java @@ -108,4 +108,7 @@ import com.google.android.exoplayer2.util.Util; return new Results(offsets, sizes, maximumSize, timestamps, flags, duration); } + private FixedSampleSizeRechunker() { + // Prevent instantiation. + } } diff --git a/library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/FragmentedMp4Extractor.java b/library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/FragmentedMp4Extractor.java index d1134dc3f6..0bf42f1839 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/FragmentedMp4Extractor.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/FragmentedMp4Extractor.java @@ -50,7 +50,6 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; -import java.util.Stack; import java.util.UUID; /** @@ -141,7 +140,7 @@ public final class FragmentedMp4Extractor implements Extractor { // Parser state. private final ParsableByteArray atomHeader; private final byte[] extendedTypeScratch; - private final Stack containerAtoms; + private final ArrayDeque containerAtoms; private final ArrayDeque pendingMetadataSampleInfos; private final @Nullable TrackOutput additionalEmsgTrackOutput; @@ -257,7 +256,7 @@ public final class FragmentedMp4Extractor implements Extractor { nalPrefix = new ParsableByteArray(5); nalBuffer = new ParsableByteArray(); extendedTypeScratch = new byte[16]; - containerAtoms = new Stack<>(); + containerAtoms = new ArrayDeque<>(); pendingMetadataSampleInfos = new ArrayDeque<>(); trackBundles = new SparseArray<>(); durationUs = C.TIME_UNSET; @@ -390,7 +389,7 @@ public final class FragmentedMp4Extractor implements Extractor { if (shouldParseContainerAtom(atomType)) { long endPosition = input.getPosition() + atomSize - Atom.HEADER_SIZE; - containerAtoms.add(new ContainerAtom(atomType, endPosition)); + containerAtoms.push(new ContainerAtom(atomType, endPosition)); if (atomSize == atomHeaderBytesRead) { processAtomEnded(endPosition); } else { diff --git a/library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/Mp4Extractor.java b/library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/Mp4Extractor.java index 75bd2c16ee..e70a49a2d7 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/Mp4Extractor.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/Mp4Extractor.java @@ -37,9 +37,9 @@ import com.google.android.exoplayer2.util.Util; import java.io.IOException; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.util.ArrayDeque; import java.util.ArrayList; import java.util.List; -import java.util.Stack; /** * Extracts data from the MP4 container format. @@ -101,7 +101,7 @@ public final class Mp4Extractor implements Extractor, SeekMap { private final ParsableByteArray nalLength; private final ParsableByteArray atomHeader; - private final Stack containerAtoms; + private final ArrayDeque containerAtoms; @State private int parserState; private int atomType; @@ -137,7 +137,7 @@ public final class Mp4Extractor implements Extractor, SeekMap { public Mp4Extractor(@Flags int flags) { this.flags = flags; atomHeader = new ParsableByteArray(Atom.LONG_HEADER_SIZE); - containerAtoms = new Stack<>(); + containerAtoms = new ArrayDeque<>(); nalStartCode = new ParsableByteArray(NalUnitUtil.NAL_START_CODE); nalLength = new ParsableByteArray(4); sampleTrackIndex = C.INDEX_UNSET; @@ -303,7 +303,7 @@ public final class Mp4Extractor implements Extractor, SeekMap { if (shouldParseContainerAtom(atomType)) { long endPosition = input.getPosition() + atomSize - atomHeaderBytesRead; - containerAtoms.add(new ContainerAtom(atomType, endPosition)); + containerAtoms.push(new ContainerAtom(atomType, endPosition)); if (atomSize == atomHeaderBytesRead) { processAtomEnded(endPosition); } else { diff --git a/library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/PsshAtomUtil.java b/library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/PsshAtomUtil.java index 84513ef4d3..a033f5c663 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/PsshAtomUtil.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/PsshAtomUtil.java @@ -49,6 +49,7 @@ public final class PsshAtomUtil { * @param data The scheme specific data. * @return The PSSH atom. */ + @SuppressWarnings("ParameterNotNullable") public static byte[] buildPsshAtom( UUID systemId, @Nullable UUID[] keyIds, @Nullable byte[] data) { boolean buildV1Atom = keyIds != null; diff --git a/library/core/src/main/java/com/google/android/exoplayer2/extractor/ogg/OpusReader.java b/library/core/src/main/java/com/google/android/exoplayer2/extractor/ogg/OpusReader.java index 8ed8a4a01d..ce3b9ea6ba 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/extractor/ogg/OpusReader.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/extractor/ogg/OpusReader.java @@ -130,6 +130,6 @@ import java.util.List; } else { length = 10000 << length; } - return frames * length; + return (long) frames * length; } } diff --git a/library/core/src/main/java/com/google/android/exoplayer2/extractor/ogg/VorbisUtil.java b/library/core/src/main/java/com/google/android/exoplayer2/extractor/ogg/VorbisUtil.java index 79767a00d8..0235fba272 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/extractor/ogg/VorbisUtil.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/extractor/ogg/VorbisUtil.java @@ -357,12 +357,12 @@ import java.util.Arrays; for (int i = 0; i < lengthMap.length; i++) { if (isSparse) { if (bitArray.readBit()) { - lengthMap[i] = bitArray.readBits(5) + 1; + lengthMap[i] = (long) (bitArray.readBits(5) + 1); } else { // entry unused lengthMap[i] = 0; } } else { // not sparse - lengthMap[i] = bitArray.readBits(5) + 1; + lengthMap[i] = (long) (bitArray.readBits(5) + 1); } } } else { @@ -392,7 +392,7 @@ import java.util.Arrays; lookupValuesCount = 0; } } else { - lookupValuesCount = entries * dimensions; + lookupValuesCount = (long) entries * dimensions; } // discard (no decoding required yet) bitArray.skipBits((int) (lookupValuesCount * valueBits)); @@ -407,6 +407,10 @@ import java.util.Arrays; return (long) Math.floor(Math.pow(entries, 1.d / dimension)); } + private VorbisUtil() { + // Prevent instantiation. + } + public static final class CodeBook { public final int dimensions; diff --git a/library/core/src/main/java/com/google/android/exoplayer2/extractor/wav/WavHeaderReader.java b/library/core/src/main/java/com/google/android/exoplayer2/extractor/wav/WavHeaderReader.java index d0810a0629..ca745591f5 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/extractor/wav/WavHeaderReader.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/extractor/wav/WavHeaderReader.java @@ -25,7 +25,7 @@ import com.google.android.exoplayer2.util.Util; import java.io.IOException; /** Reads a {@code WavHeader} from an input stream; supports resuming from input failures. */ -/*package*/ final class WavHeaderReader { +/* package */ final class WavHeaderReader { private static final String TAG = "WavHeaderReader"; @@ -158,6 +158,10 @@ import java.io.IOException; wavHeader.setDataBounds(input.getPosition(), chunkHeader.size); } + private WavHeaderReader() { + // Prevent instantiation. + } + /** Container for a WAV chunk header. */ private static final class ChunkHeader { diff --git a/library/core/src/main/java/com/google/android/exoplayer2/offline/ProgressiveDownloader.java b/library/core/src/main/java/com/google/android/exoplayer2/offline/ProgressiveDownloader.java index cf64d26bb5..8c80a23d67 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/offline/ProgressiveDownloader.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/offline/ProgressiveDownloader.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.google.android.exoplayer2.offline; +package com.google.android.exoplayer2.offline; import android.net.Uri; import com.google.android.exoplayer2.C; diff --git a/library/core/src/main/java/com/google/android/exoplayer2/offline/SegmentDownloader.java b/library/core/src/main/java/com/google/android/exoplayer2/offline/SegmentDownloader.java index 6ce2121acd..9be694264c 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/offline/SegmentDownloader.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/offline/SegmentDownloader.java @@ -201,6 +201,8 @@ public abstract class SegmentDownloader, K> throws InterruptedException, IOException; /** Initializes the download, returning a list of {@link Segment}s that need to be downloaded. */ + // Writes to downloadedSegments and downloadedBytes are safe. See the comment on download(). + @SuppressWarnings("NonAtomicVolatileUpdate") private List initDownload() throws IOException, InterruptedException { M manifest = getManifest(dataSource, manifestUri); if (!streamKeys.isEmpty()) { diff --git a/library/core/src/main/java/com/google/android/exoplayer2/source/TrackGroup.java b/library/core/src/main/java/com/google/android/exoplayer2/source/TrackGroup.java index a9fb261768..56c9989f34 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/source/TrackGroup.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/source/TrackGroup.java @@ -72,11 +72,14 @@ public final class TrackGroup implements Parcelable { } /** - * Returns the index of the track with the given format in the group. + * Returns the index of the track with the given format in the group. The format is located by + * identity so, for example, {@code group.indexOf(group.getFormat(index)) == index} even if + * multiple tracks have formats that contain the same values. * * @param format The format. * @return The index of the track, or {@link C#INDEX_UNSET} if no such track exists. */ + @SuppressWarnings("ReferenceEquality") public int indexOf(Format format) { for (int i = 0; i < formats.length; i++) { if (format == formats[i]) { diff --git a/library/core/src/main/java/com/google/android/exoplayer2/text/cea/Cea708Decoder.java b/library/core/src/main/java/com/google/android/exoplayer2/text/cea/Cea708Decoder.java index 6bdbebc73b..731c9032d6 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/text/cea/Cea708Decoder.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/text/cea/Cea708Decoder.java @@ -38,7 +38,6 @@ import com.google.android.exoplayer2.util.ParsableBitArray; import com.google.android.exoplayer2.util.ParsableByteArray; import java.util.ArrayList; import java.util.Collections; -import java.util.LinkedList; import java.util.List; /** @@ -196,7 +195,10 @@ public final class Cea708Decoder extends CeaDecoder { @Override protected void decode(SubtitleInputBuffer inputBuffer) { - ccData.reset(inputBuffer.data.array(), inputBuffer.data.limit()); + // Subtitle input buffers are non-direct and the position is zero, so calling array() is safe. + @SuppressWarnings("ByteBufferBackingArray") + byte[] inputBufferData = inputBuffer.data.array(); + ccData.reset(inputBufferData, inputBuffer.data.limit()); while (ccData.bytesLeft() >= 3) { int ccTypeAndValid = (ccData.readUnsignedByte() & 0x07); @@ -879,7 +881,7 @@ public final class Cea708Decoder extends CeaDecoder { private int row; public CueBuilder() { - rolledUpCaptions = new LinkedList<>(); + rolledUpCaptions = new ArrayList<>(); captionStringBuilder = new SpannableStringBuilder(); reset(); } diff --git a/library/core/src/main/java/com/google/android/exoplayer2/text/cea/CeaDecoder.java b/library/core/src/main/java/com/google/android/exoplayer2/text/cea/CeaDecoder.java index 07a55f1a40..3efc16bdd0 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/text/cea/CeaDecoder.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/text/cea/CeaDecoder.java @@ -24,7 +24,7 @@ import com.google.android.exoplayer2.text.SubtitleDecoderException; import com.google.android.exoplayer2.text.SubtitleInputBuffer; import com.google.android.exoplayer2.text.SubtitleOutputBuffer; import com.google.android.exoplayer2.util.Assertions; -import java.util.LinkedList; +import java.util.ArrayDeque; import java.util.PriorityQueue; /** @@ -35,8 +35,8 @@ import java.util.PriorityQueue; private static final int NUM_INPUT_BUFFERS = 10; private static final int NUM_OUTPUT_BUFFERS = 2; - private final LinkedList availableInputBuffers; - private final LinkedList availableOutputBuffers; + private final ArrayDeque availableInputBuffers; + private final ArrayDeque availableOutputBuffers; private final PriorityQueue queuedInputBuffers; private CeaInputBuffer dequeuedInputBuffer; @@ -44,11 +44,11 @@ import java.util.PriorityQueue; private long queuedInputBufferCount; public CeaDecoder() { - availableInputBuffers = new LinkedList<>(); + availableInputBuffers = new ArrayDeque<>(); for (int i = 0; i < NUM_INPUT_BUFFERS; i++) { availableInputBuffers.add(new CeaInputBuffer()); } - availableOutputBuffers = new LinkedList<>(); + availableOutputBuffers = new ArrayDeque<>(); for (int i = 0; i < NUM_OUTPUT_BUFFERS; i++) { availableOutputBuffers.add(new CeaOutputBuffer()); } diff --git a/library/core/src/main/java/com/google/android/exoplayer2/text/ssa/SsaDecoder.java b/library/core/src/main/java/com/google/android/exoplayer2/text/ssa/SsaDecoder.java index 0cb6f66898..e528a57762 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/text/ssa/SsaDecoder.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/text/ssa/SsaDecoder.java @@ -62,7 +62,7 @@ public final class SsaDecoder extends SimpleSubtitleDecoder { super("SsaDecoder"); if (initializationData != null && !initializationData.isEmpty()) { haveInitializationData = true; - String formatLine = new String(initializationData.get(0)); + String formatLine = Util.fromUtf8Bytes(initializationData.get(0)); Assertions.checkArgument(formatLine.startsWith(FORMAT_LINE_PREFIX)); parseFormatLine(formatLine); parseHeader(new ParsableByteArray(initializationData.get(1))); diff --git a/library/core/src/main/java/com/google/android/exoplayer2/text/ttml/TtmlDecoder.java b/library/core/src/main/java/com/google/android/exoplayer2/text/ttml/TtmlDecoder.java index ad8f849c60..61e0085065 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/text/ttml/TtmlDecoder.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/text/ttml/TtmlDecoder.java @@ -26,8 +26,8 @@ import com.google.android.exoplayer2.util.Util; import com.google.android.exoplayer2.util.XmlPullParserUtil; import java.io.ByteArrayInputStream; import java.io.IOException; +import java.util.ArrayDeque; import java.util.HashMap; -import java.util.LinkedList; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -109,13 +109,13 @@ public final class TtmlDecoder extends SimpleSubtitleDecoder { ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes, 0, length); xmlParser.setInput(inputStream, null); TtmlSubtitle ttmlSubtitle = null; - LinkedList nodeStack = new LinkedList<>(); + ArrayDeque nodeStack = new ArrayDeque<>(); int unsupportedNodeDepth = 0; int eventType = xmlParser.getEventType(); FrameAndTickRate frameAndTickRate = DEFAULT_FRAME_AND_TICK_RATE; CellResolution cellResolution = DEFAULT_CELL_RESOLUTION; while (eventType != XmlPullParser.END_DOCUMENT) { - TtmlNode parent = nodeStack.peekLast(); + TtmlNode parent = nodeStack.peek(); if (unsupportedNodeDepth == 0) { String name = xmlParser.getName(); if (eventType == XmlPullParser.START_TAG) { @@ -131,7 +131,7 @@ public final class TtmlDecoder extends SimpleSubtitleDecoder { } else { try { TtmlNode node = parseNode(xmlParser, parent, regionMap, frameAndTickRate); - nodeStack.addLast(node); + nodeStack.push(node); if (parent != null) { parent.addChild(node); } @@ -145,9 +145,9 @@ public final class TtmlDecoder extends SimpleSubtitleDecoder { parent.addChild(TtmlNode.buildTextNode(xmlParser.getText())); } else if (eventType == XmlPullParser.END_TAG) { if (xmlParser.getName().equals(TtmlNode.TAG_TT)) { - ttmlSubtitle = new TtmlSubtitle(nodeStack.getLast(), globalStyles, regionMap); + ttmlSubtitle = new TtmlSubtitle(nodeStack.peek(), globalStyles, regionMap); } - nodeStack.removeLast(); + nodeStack.pop(); } } else { if (eventType == XmlPullParser.START_TAG) { @@ -178,7 +178,7 @@ public final class TtmlDecoder extends SimpleSubtitleDecoder { float frameRateMultiplier = 1; String frameRateMultiplierString = xmlParser.getAttributeValue(TTP, "frameRateMultiplier"); if (frameRateMultiplierString != null) { - String[] parts = frameRateMultiplierString.split(" "); + String[] parts = Util.split(frameRateMultiplierString, " "); if (parts.length != 2) { throw new SubtitleDecoderException("frameRateMultiplier doesn't have 2 parts"); } @@ -354,7 +354,8 @@ public final class TtmlDecoder extends SimpleSubtitleDecoder { } private String[] parseStyleIds(String parentStyleIds) { - return parentStyleIds.split("\\s+"); + parentStyleIds = parentStyleIds.trim(); + return parentStyleIds.isEmpty() ? new String[0] : Util.split(parentStyleIds, "\\s+"); } private TtmlStyle parseStyleAttributes(XmlPullParser parser, TtmlStyle style) { @@ -531,7 +532,7 @@ public final class TtmlDecoder extends SimpleSubtitleDecoder { private static void parseFontSize(String expression, TtmlStyle out) throws SubtitleDecoderException { - String[] expressions = expression.split("\\s+"); + String[] expressions = Util.split(expression, "\\s+"); Matcher matcher; if (expressions.length == 1) { matcher = FONT_SIZE.matcher(expression); diff --git a/library/core/src/main/java/com/google/android/exoplayer2/text/tx3g/Tx3gDecoder.java b/library/core/src/main/java/com/google/android/exoplayer2/text/tx3g/Tx3gDecoder.java index 2270ccc632..ebc38bcd70 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/text/tx3g/Tx3gDecoder.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/text/tx3g/Tx3gDecoder.java @@ -92,7 +92,8 @@ public final class Tx3gDecoder extends SimpleSubtitleDecoder { | ((initializationBytes[27] & 0xFF) << 16) | ((initializationBytes[28] & 0xFF) << 8) | (initializationBytes[29] & 0xFF); - String fontFamily = new String(initializationBytes, 43, initializationBytes.length - 43); + String fontFamily = + Util.fromUtf8Bytes(initializationBytes, 43, initializationBytes.length - 43); defaultFontFamily = TX3G_SERIF.equals(fontFamily) ? C.SERIF_NAME : C.SANS_SERIF_NAME; //font size (initializationBytes[25]) is 5% of video height calculatedVideoTrackHeight = 20 * initializationBytes[25]; diff --git a/library/core/src/main/java/com/google/android/exoplayer2/text/webvtt/CssParser.java b/library/core/src/main/java/com/google/android/exoplayer2/text/webvtt/CssParser.java index ea1e6891f0..81c362bda5 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/text/webvtt/CssParser.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/text/webvtt/CssParser.java @@ -18,6 +18,7 @@ package com.google.android.exoplayer2.text.webvtt; import android.text.TextUtils; import com.google.android.exoplayer2.util.ColorParser; import com.google.android.exoplayer2.util.ParsableByteArray; +import com.google.android.exoplayer2.util.Util; import java.util.Arrays; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -314,7 +315,7 @@ import java.util.regex.Pattern; } selector = selector.substring(0, voiceStartIndex); } - String[] classDivision = selector.split("\\."); + String[] classDivision = Util.split(selector, "\\."); String tagAndIdDivision = classDivision[0]; int idPrefixIndex = tagAndIdDivision.indexOf('#'); if (idPrefixIndex != -1) { diff --git a/library/core/src/main/java/com/google/android/exoplayer2/text/webvtt/Mp4WebvttDecoder.java b/library/core/src/main/java/com/google/android/exoplayer2/text/webvtt/Mp4WebvttDecoder.java index 159dd4f2e0..17c2366f07 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/text/webvtt/Mp4WebvttDecoder.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/text/webvtt/Mp4WebvttDecoder.java @@ -78,7 +78,8 @@ public final class Mp4WebvttDecoder extends SimpleSubtitleDecoder { int boxType = sampleData.readInt(); remainingCueBoxBytes -= BOX_HEADER_SIZE; int payloadLength = boxSize - BOX_HEADER_SIZE; - String boxPayload = new String(sampleData.data, sampleData.getPosition(), payloadLength); + String boxPayload = + Util.fromUtf8Bytes(sampleData.data, sampleData.getPosition(), payloadLength); sampleData.skipBytes(payloadLength); remainingCueBoxBytes -= payloadLength; if (boxType == TYPE_sttg) { diff --git a/library/core/src/main/java/com/google/android/exoplayer2/text/webvtt/WebvttCueParser.java b/library/core/src/main/java/com/google/android/exoplayer2/text/webvtt/WebvttCueParser.java index 80ebecdc0e..6f2a1328c6 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/text/webvtt/WebvttCueParser.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/text/webvtt/WebvttCueParser.java @@ -34,11 +34,12 @@ import android.text.style.UnderlineSpan; import android.util.Log; import com.google.android.exoplayer2.text.Cue; import com.google.android.exoplayer2.util.ParsableByteArray; +import com.google.android.exoplayer2.util.Util; +import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; -import java.util.Stack; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -157,7 +158,7 @@ public final class WebvttCueParser { /* package */ static void parseCueText(String id, String markup, WebvttCue.Builder builder, List styles) { SpannableStringBuilder spannedText = new SpannableStringBuilder(); - Stack startTagStack = new Stack<>(); + ArrayDeque startTagStack = new ArrayDeque<>(); List scratchStyleMatches = new ArrayList<>(); int pos = 0; while (pos < markup.length()) { @@ -456,7 +457,7 @@ public final class WebvttCueParser { if (tagExpression.isEmpty()) { return null; } - return tagExpression.split("[ \\.]")[0]; + return Util.splitAtFirst(tagExpression, "[ \\.]")[0]; } private static void getApplicableStyles(List declaredStyles, String id, @@ -518,7 +519,7 @@ public final class WebvttCueParser { voice = fullTagExpression.substring(voiceStartIndex).trim(); fullTagExpression = fullTagExpression.substring(0, voiceStartIndex); } - String[] nameAndClasses = fullTagExpression.split("\\."); + String[] nameAndClasses = Util.split(fullTagExpression, "\\."); String name = nameAndClasses[0]; String[] classes; if (nameAndClasses.length > 1) { diff --git a/library/core/src/main/java/com/google/android/exoplayer2/text/webvtt/WebvttParserUtil.java b/library/core/src/main/java/com/google/android/exoplayer2/text/webvtt/WebvttParserUtil.java index d0c3eda494..b94be19d8f 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/text/webvtt/WebvttParserUtil.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/text/webvtt/WebvttParserUtil.java @@ -17,6 +17,7 @@ package com.google.android.exoplayer2.text.webvtt; import com.google.android.exoplayer2.text.SubtitleDecoderException; import com.google.android.exoplayer2.util.ParsableByteArray; +import com.google.android.exoplayer2.util.Util; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -53,8 +54,8 @@ public final class WebvttParserUtil { */ public static long parseTimestampUs(String timestamp) throws NumberFormatException { long value = 0; - String[] parts = timestamp.split("\\.", 2); - String[] subparts = parts[0].split(":"); + String[] parts = Util.splitAtFirst(timestamp, "\\."); + String[] subparts = Util.split(parts[0], ":"); for (String subpart : subparts) { value = (value * 60) + Long.parseLong(subpart); } diff --git a/library/core/src/main/java/com/google/android/exoplayer2/trackselection/BaseTrackSelection.java b/library/core/src/main/java/com/google/android/exoplayer2/trackselection/BaseTrackSelection.java index 81eb5dd888..3f201bccea 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/trackselection/BaseTrackSelection.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/trackselection/BaseTrackSelection.java @@ -110,6 +110,7 @@ public abstract class BaseTrackSelection implements TrackSelection { } @Override + @SuppressWarnings("ReferenceEquality") public final int indexOf(Format format) { for (int i = 0; i < length; i++) { if (formats[i] == format) { @@ -183,7 +184,9 @@ public abstract class BaseTrackSelection implements TrackSelection { return hashCode; } + // Track groups are compared by identity not value, as distinct groups may have the same value. @Override + @SuppressWarnings("ReferenceEquality") public boolean equals(@Nullable Object obj) { if (this == obj) { return true; diff --git a/library/core/src/main/java/com/google/android/exoplayer2/trackselection/TrackSelection.java b/library/core/src/main/java/com/google/android/exoplayer2/trackselection/TrackSelection.java index 58616996ff..ee0a397a8d 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/trackselection/TrackSelection.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/trackselection/TrackSelection.java @@ -91,7 +91,9 @@ public interface TrackSelection { int getIndexInTrackGroup(int index); /** - * Returns the index in the selection of the track with the specified format. + * Returns the index in the selection of the track with the specified format. The format is + * located by identity so, for example, {@code selection.indexOf(selection.getFormat(index)) == + * index} even if multiple selected tracks have formats that contain the same values. * * @param format The format. * @return The index in the selection, or {@link C#INDEX_UNSET} if the track with the specified diff --git a/library/core/src/main/java/com/google/android/exoplayer2/upstream/DataSchemeDataSource.java b/library/core/src/main/java/com/google/android/exoplayer2/upstream/DataSchemeDataSource.java index c547625819..33d67f3f46 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/upstream/DataSchemeDataSource.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/upstream/DataSchemeDataSource.java @@ -19,6 +19,7 @@ import android.net.Uri; import android.util.Base64; import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.ParserException; +import com.google.android.exoplayer2.util.Util; import java.io.IOException; import java.net.URLDecoder; @@ -41,8 +42,8 @@ public final class DataSchemeDataSource implements DataSource { if (!SCHEME_DATA.equals(scheme)) { throw new ParserException("Unsupported scheme: " + scheme); } - String[] uriParts = uri.getSchemeSpecificPart().split(","); - if (uriParts.length > 2) { + String[] uriParts = Util.split(uri.getSchemeSpecificPart(), ","); + if (uriParts.length != 2) { throw new ParserException("Unexpected URI format: " + uri); } String dataString = uriParts[1]; diff --git a/library/core/src/main/java/com/google/android/exoplayer2/upstream/cache/ContentMetadataInternal.java b/library/core/src/main/java/com/google/android/exoplayer2/upstream/cache/ContentMetadataInternal.java index 3376dd6944..0065018260 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/upstream/cache/ContentMetadataInternal.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/upstream/cache/ContentMetadataInternal.java @@ -20,7 +20,7 @@ import android.support.annotation.Nullable; import com.google.android.exoplayer2.C; /** Helper classes to easily access and modify internal metadata values. */ -/*package*/ final class ContentMetadataInternal { +/* package */ final class ContentMetadataInternal { private static final String PREFIX = ContentMetadata.INTERNAL_METADATA_NAME_PREFIX; private static final String METADATA_NAME_REDIRECTED_URI = PREFIX + "redir"; @@ -59,4 +59,8 @@ import com.google.android.exoplayer2.C; public static void removeRedirectedUri(ContentMetadataMutations mutations) { mutations.remove(METADATA_NAME_REDIRECTED_URI); } + + private ContentMetadataInternal() { + // Prevent instantiation. + } } diff --git a/library/core/src/main/java/com/google/android/exoplayer2/upstream/crypto/AesFlushingCipher.java b/library/core/src/main/java/com/google/android/exoplayer2/upstream/crypto/AesFlushingCipher.java index e093eb3064..1721b1d8b7 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/upstream/crypto/AesFlushingCipher.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/upstream/crypto/AesFlushingCipher.java @@ -16,6 +16,7 @@ package com.google.android.exoplayer2.upstream.crypto; import com.google.android.exoplayer2.util.Assertions; +import com.google.android.exoplayer2.util.Util; import java.nio.ByteBuffer; import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; @@ -49,7 +50,9 @@ public final class AesFlushingCipher { flushedBlock = new byte[blockSize]; long counter = offset / blockSize; int startPadding = (int) (offset % blockSize); - cipher.init(mode, new SecretKeySpec(secretKey, cipher.getAlgorithm().split("/")[0]), + cipher.init( + mode, + new SecretKeySpec(secretKey, Util.splitAtFirst(cipher.getAlgorithm(), "/")[0]), new IvParameterSpec(getInitializationVector(nonce, counter))); if (startPadding != 0) { updateInPlace(new byte[startPadding], 0, startPadding); diff --git a/library/core/src/main/java/com/google/android/exoplayer2/util/ColorParser.java b/library/core/src/main/java/com/google/android/exoplayer2/util/ColorParser.java index a9df80e9fe..54f52e0a14 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/util/ColorParser.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/util/ColorParser.java @@ -26,7 +26,7 @@ import java.util.regex.Pattern; * * @see WebVTT CSS Styling * @see Timed Text Markup Language 2 (TTML2) - 10.3.5 - **/ + */ public final class ColorParser { private static final String RGB = "rgb"; @@ -271,4 +271,7 @@ public final class ColorParser { COLOR_MAP.put("yellowgreen", 0xFF9ACD32); } + private ColorParser() { + // Prevent instantiation. + } } diff --git a/library/core/src/main/java/com/google/android/exoplayer2/util/MimeTypes.java b/library/core/src/main/java/com/google/android/exoplayer2/util/MimeTypes.java index d13aa877e0..9e9ff5fd77 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/util/MimeTypes.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/util/MimeTypes.java @@ -144,7 +144,7 @@ public final class MimeTypes { if (codecs == null) { return null; } - String[] codecList = codecs.split(","); + String[] codecList = Util.split(codecs, ","); for (String codec : codecList) { String mimeType = getMediaMimeType(codec); if (mimeType != null && isVideo(mimeType)) { @@ -164,7 +164,7 @@ public final class MimeTypes { if (codecs == null) { return null; } - String[] codecList = codecs.split(","); + String[] codecList = Util.split(codecs, ","); for (String codec : codecList) { String mimeType = getMediaMimeType(codec); if (mimeType != null && isAudio(mimeType)) { diff --git a/library/core/src/main/java/com/google/android/exoplayer2/util/ParsableBitArray.java b/library/core/src/main/java/com/google/android/exoplayer2/util/ParsableBitArray.java index fb5f9525e9..c60caf9ba8 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/util/ParsableBitArray.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/util/ParsableBitArray.java @@ -175,7 +175,7 @@ public final class ParsableBitArray { bitOffset -= 8; returnValue |= (data[byteOffset++] & 0xFF) << bitOffset; } - returnValue |= (data[byteOffset] & 0xFF) >> 8 - bitOffset; + returnValue |= (data[byteOffset] & 0xFF) >> (8 - bitOffset); returnValue &= 0xFFFFFFFF >>> (32 - numBits); if (bitOffset == 8) { bitOffset = 0; @@ -199,17 +199,18 @@ public final class ParsableBitArray { int to = offset + (numBits >> 3) /* numBits / 8 */; for (int i = offset; i < to; i++) { buffer[i] = (byte) (data[byteOffset++] << bitOffset); - buffer[i] |= (data[byteOffset] & 0xFF) >> (8 - bitOffset); + buffer[i] = (byte) (buffer[i] | ((data[byteOffset] & 0xFF) >> (8 - bitOffset))); } // Trailing bits. int bitsLeft = numBits & 7 /* numBits % 8 */; if (bitsLeft == 0) { return; } - buffer[to] &= 0xFF >> bitsLeft; // Set to 0 the bits that are going to be overwritten. + // Set bits that are going to be overwritten to 0. + buffer[to] = (byte) (buffer[to] & (0xFF >> bitsLeft)); if (bitOffset + bitsLeft > 8) { // We read the rest of data[byteOffset] and increase byteOffset. - buffer[to] |= (byte) ((data[byteOffset++] & 0xFF) << bitOffset); + buffer[to] = (byte) (buffer[to] | ((data[byteOffset++] & 0xFF) << bitOffset)); bitOffset -= 8; } bitOffset += bitsLeft; @@ -280,9 +281,10 @@ public final class ParsableBitArray { int firstByteReadSize = Math.min(8 - bitOffset, numBits); int firstByteRightPaddingSize = 8 - bitOffset - firstByteReadSize; int firstByteBitmask = (0xFF00 >> bitOffset) | ((1 << firstByteRightPaddingSize) - 1); - data[byteOffset] &= firstByteBitmask; + data[byteOffset] = (byte) (data[byteOffset] & firstByteBitmask); int firstByteInputBits = value >>> (numBits - firstByteReadSize); - data[byteOffset] |= firstByteInputBits << firstByteRightPaddingSize; + data[byteOffset] = + (byte) (data[byteOffset] | (firstByteInputBits << firstByteRightPaddingSize)); remainingBitsToRead -= firstByteReadSize; int currentByteIndex = byteOffset + 1; while (remainingBitsToRead > 8) { @@ -290,9 +292,11 @@ public final class ParsableBitArray { remainingBitsToRead -= 8; } int lastByteRightPaddingSize = 8 - remainingBitsToRead; - data[currentByteIndex] &= (1 << lastByteRightPaddingSize) - 1; + data[currentByteIndex] = + (byte) (data[currentByteIndex] & ((1 << lastByteRightPaddingSize) - 1)); int lastByteInput = value & ((1 << remainingBitsToRead) - 1); - data[currentByteIndex] |= lastByteInput << lastByteRightPaddingSize; + data[currentByteIndex] = + (byte) (data[currentByteIndex] | (lastByteInput << lastByteRightPaddingSize)); skipBits(numBits); assertValidOffset(); } diff --git a/library/core/src/main/java/com/google/android/exoplayer2/util/ParsableByteArray.java b/library/core/src/main/java/com/google/android/exoplayer2/util/ParsableByteArray.java index 57313ea895..5190896d9f 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/util/ParsableByteArray.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/util/ParsableByteArray.java @@ -470,7 +470,7 @@ public final class ParsableByteArray { if (lastIndex < limit && data[lastIndex] == 0) { stringLength--; } - String result = new String(data, position, stringLength); + String result = Util.fromUtf8Bytes(data, position, stringLength); position += length; return result; } @@ -489,7 +489,7 @@ public final class ParsableByteArray { while (stringLimit < limit && data[stringLimit] != 0) { stringLimit++; } - String string = new String(data, position, stringLimit - position); + String string = Util.fromUtf8Bytes(data, position, stringLimit - position); position = stringLimit; if (position < limit) { position++; @@ -520,7 +520,7 @@ public final class ParsableByteArray { // There's a byte order mark at the start of the line. Discard it. position += 3; } - String line = new String(data, position, lineLimit - position); + String line = Util.fromUtf8Bytes(data, position, lineLimit - position); position = lineLimit; if (position == limit) { return line; diff --git a/library/core/src/main/java/com/google/android/exoplayer2/util/ParsableNalUnitBitArray.java b/library/core/src/main/java/com/google/android/exoplayer2/util/ParsableNalUnitBitArray.java index 443c69909c..3a7202c674 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/util/ParsableNalUnitBitArray.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/util/ParsableNalUnitBitArray.java @@ -140,7 +140,7 @@ public final class ParsableNalUnitBitArray { returnValue |= (data[byteOffset] & 0xFF) << bitOffset; byteOffset += shouldSkipByte(byteOffset + 1) ? 2 : 1; } - returnValue |= (data[byteOffset] & 0xFF) >> 8 - bitOffset; + returnValue |= (data[byteOffset] & 0xFF) >> (8 - bitOffset); returnValue &= 0xFFFFFFFF >>> (32 - numBits); if (bitOffset == 8) { bitOffset = 0; diff --git a/library/core/src/main/java/com/google/android/exoplayer2/util/Util.java b/library/core/src/main/java/com/google/android/exoplayer2/util/Util.java index 37e3e119bf..90c5d17b6d 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/util/Util.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/util/Util.java @@ -332,6 +332,18 @@ public final class Util { return new String(bytes, Charset.forName(C.UTF8_NAME)); } + /** + * Returns a new {@link String} constructed by decoding UTF-8 encoded bytes in a subarray. + * + * @param bytes The UTF-8 encoded bytes to decode. + * @param offset The index of the first byte to decode. + * @param length The number of bytes to decode. + * @return The string. + */ + public static String fromUtf8Bytes(byte[] bytes, int offset, int length) { + return new String(bytes, offset, length, Charset.forName(C.UTF8_NAME)); + } + /** * Returns a new byte array containing the code points of a {@link String} encoded using UTF-8. * @@ -342,6 +354,33 @@ public final class Util { return value.getBytes(Charset.forName(C.UTF8_NAME)); } + /** + * Splits a string using {@code value.split(regex, -1}). Note: this is is similar to {@link + * String#split(String)} but empty matches at the end of the string will not be omitted from the + * returned array. + * + * @param value The string to split. + * @param regex A delimiting regular expression. + * @return The array of strings resulting from splitting the string. + */ + public static String[] split(String value, String regex) { + return value.split(regex, /* limit= */ -1); + } + + /** + * Splits the string at the first occurrence of the delimiter {@code regex}. If the delimiter does + * not match, returns an array with one element which is the input string. If the delimiter does + * match, returns an array with the portion of the string before the delimiter and the rest of the + * string. + * + * @param value The string. + * @param regex A delimiting regular expression. + * @return The string split by the first occurrence of the delimiter. + */ + public static String[] splitAtFirst(String value, String regex) { + return value.split(regex, /* limit= */ 2); + } + /** * Returns whether the given character is a carriage return ('\r') or a line feed ('\n'). * @@ -978,7 +1017,7 @@ public final class Util { if (TextUtils.isEmpty(codecs)) { return null; } - String[] codecArray = codecs.trim().split("(\\s*,\\s*)"); + String[] codecArray = split(codecs.trim(), "(\\s*,\\s*)"); StringBuilder builder = new StringBuilder(); for (String codec : codecArray) { if (trackType == MimeTypes.getTrackTypeOfCodec(codec)) { @@ -1454,7 +1493,7 @@ public final class Util { // If we managed to read sys.display-size, attempt to parse it. if (!TextUtils.isEmpty(sysDisplaySize)) { try { - String[] sysDisplaySizeParts = sysDisplaySize.trim().split("x"); + String[] sysDisplaySizeParts = split(sysDisplaySize.trim(), "x"); if (sysDisplaySizeParts.length == 2) { int width = Integer.parseInt(sysDisplaySizeParts[0]); int height = Integer.parseInt(sysDisplaySizeParts[1]);