diff --git a/library/core/src/main/java/com/google/android/exoplayer2/upstream/cache/CacheDataSink.java b/library/core/src/main/java/com/google/android/exoplayer2/upstream/cache/CacheDataSink.java index 22ed3892ec..89de5fb343 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/upstream/cache/CacheDataSink.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/upstream/cache/CacheDataSink.java @@ -15,6 +15,7 @@ */ package com.google.android.exoplayer2.upstream.cache; +import androidx.annotation.Nullable; import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.upstream.DataSink; import com.google.android.exoplayer2.upstream.DataSpec; @@ -27,6 +28,7 @@ import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; +import org.checkerframework.checker.nullness.qual.MonotonicNonNull; /** * Writes data into a cache. @@ -49,13 +51,13 @@ public final class CacheDataSink implements DataSink { private final long fragmentSize; private final int bufferSize; - private DataSpec dataSpec; + @Nullable private DataSpec dataSpec; private long dataSpecFragmentSize; - private File file; - private OutputStream outputStream; + @Nullable private File file; + @Nullable private OutputStream outputStream; private long outputStreamBytesWritten; private long dataSpecBytesWritten; - private ReusableBufferedOutputStream bufferedOutputStream; + @MonotonicNonNull private ReusableBufferedOutputStream bufferedOutputStream; /** * Thrown when IOException is encountered when writing data into sink. diff --git a/library/core/src/main/java/com/google/android/exoplayer2/upstream/cache/CacheDataSource.java b/library/core/src/main/java/com/google/android/exoplayer2/upstream/cache/CacheDataSource.java index 541c3b2d9d..c51f75116f 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/upstream/cache/CacheDataSource.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/upstream/cache/CacheDataSource.java @@ -377,7 +377,7 @@ public final class CacheDataSource implements DataSource { * reading from {@link #upstreamDataSource}, which is the currently open source. */ private void openNextSource(boolean checkCache) throws IOException { - CacheSpan nextSpan; + @Nullable CacheSpan nextSpan; if (currentRequestIgnoresCache) { nextSpan = null; } else if (blockOnCache) { @@ -487,7 +487,7 @@ public final class CacheDataSource implements DataSource { } private static Uri getRedirectedUriOrDefault(Cache cache, String key, Uri defaultUri) { - Uri redirectedUri = ContentMetadata.getRedirectedUri(cache.getContentMetadata(key)); + @Nullable Uri redirectedUri = ContentMetadata.getRedirectedUri(cache.getContentMetadata(key)); return redirectedUri != null ? redirectedUri : defaultUri; } diff --git a/library/core/src/main/java/com/google/android/exoplayer2/upstream/cache/CacheFileMetadataIndex.java b/library/core/src/main/java/com/google/android/exoplayer2/upstream/cache/CacheFileMetadataIndex.java index dc27dec363..b2de05ca77 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/upstream/cache/CacheFileMetadataIndex.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/upstream/cache/CacheFileMetadataIndex.java @@ -60,7 +60,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; private final DatabaseProvider databaseProvider; - private @MonotonicNonNull String tableName; + @MonotonicNonNull private String tableName; /** * Deletes index data for the specified cache. diff --git a/library/core/src/main/java/com/google/android/exoplayer2/upstream/cache/CacheKeyFactory.java b/library/core/src/main/java/com/google/android/exoplayer2/upstream/cache/CacheKeyFactory.java index bfa404c074..3401d6f575 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/upstream/cache/CacheKeyFactory.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/upstream/cache/CacheKeyFactory.java @@ -24,6 +24,7 @@ public interface CacheKeyFactory { * Returns a cache key for the given {@link DataSpec}. * * @param dataSpec The data being cached. + * @return The cache key. */ String buildCacheKey(DataSpec dataSpec); } diff --git a/library/core/src/main/java/com/google/android/exoplayer2/upstream/cache/CacheUtil.java b/library/core/src/main/java/com/google/android/exoplayer2/upstream/cache/CacheUtil.java index ce16ea2439..fadffd9b95 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/upstream/cache/CacheUtil.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/upstream/cache/CacheUtil.java @@ -183,7 +183,7 @@ public final class CacheUtil { String key = buildCacheKey(dataSpec, cacheKeyFactory); long bytesLeft; - ProgressNotifier progressNotifier = null; + @Nullable ProgressNotifier progressNotifier = null; if (progressListener != null) { progressNotifier = new ProgressNotifier(progressListener); Pair lengthAndBytesAlreadyCached = getCached(dataSpec, cache, cacheKeyFactory); @@ -373,7 +373,7 @@ public final class CacheUtil { } /* package */ static boolean isCausedByPositionOutOfRange(IOException e) { - Throwable cause = e; + @Nullable Throwable cause = e; while (cause != null) { if (cause instanceof DataSourceException) { int reason = ((DataSourceException) cause).reason; diff --git a/library/core/src/main/java/com/google/android/exoplayer2/upstream/cache/CachedContentIndex.java b/library/core/src/main/java/com/google/android/exoplayer2/upstream/cache/CachedContentIndex.java index 7e09025ddd..1b9b4a3629 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/upstream/cache/CachedContentIndex.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/upstream/cache/CachedContentIndex.java @@ -229,11 +229,12 @@ import org.checkerframework.checker.nullness.compatqual.NullableType; * @return A new or existing CachedContent instance with the given key. */ public CachedContent getOrAdd(String key) { - CachedContent cachedContent = keyToContent.get(key); + @Nullable CachedContent cachedContent = keyToContent.get(key); return cachedContent == null ? addNew(key) : cachedContent; } /** Returns a CachedContent instance with the given key or null if there isn't one. */ + @Nullable public CachedContent get(String key) { return keyToContent.get(key); } @@ -254,14 +255,15 @@ import org.checkerframework.checker.nullness.compatqual.NullableType; return getOrAdd(key).id; } - /** Returns the key which has the given id assigned. */ + /** Returns the key which has the given id assigned, or {@code null} if no such key exists. */ + @Nullable public String getKeyForId(int id) { return idToKey.get(id); } /** Removes {@link CachedContent} with the given key from index if it's empty and not locked. */ public void maybeRemove(String key) { - CachedContent cachedContent = keyToContent.get(key); + @Nullable CachedContent cachedContent = keyToContent.get(key); if (cachedContent != null && cachedContent.isEmpty() && !cachedContent.isLocked()) { keyToContent.remove(key); int id = cachedContent.id; @@ -626,7 +628,7 @@ import org.checkerframework.checker.nullness.compatqual.NullableType; } private void writeFile(HashMap content) throws IOException { - DataOutputStream output = null; + @Nullable DataOutputStream output = null; try { OutputStream outputStream = atomicFile.startWrite(); if (bufferedOutputStream == null) { diff --git a/library/core/src/main/java/com/google/android/exoplayer2/upstream/cache/CachedRegionTracker.java b/library/core/src/main/java/com/google/android/exoplayer2/upstream/cache/CachedRegionTracker.java index fb2d4f694f..15a827ba74 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/upstream/cache/CachedRegionTracker.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/upstream/cache/CachedRegionTracker.java @@ -16,6 +16,7 @@ package com.google.android.exoplayer2.upstream.cache; import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import com.google.android.exoplayer2.extractor.ChunkIndex; import com.google.android.exoplayer2.util.Log; import com.google.android.exoplayer2.util.Util; @@ -77,7 +78,7 @@ public final class CachedRegionTracker implements Cache.Listener { */ public synchronized int getRegionEndTimeMs(long byteOffset) { lookupRegion.startOffset = byteOffset; - Region floorRegion = regions.floor(lookupRegion); + @Nullable Region floorRegion = regions.floor(lookupRegion); if (floorRegion == null || byteOffset > floorRegion.endOffset || floorRegion.endOffsetIndex == -1) { return NOT_CACHED; @@ -102,7 +103,7 @@ public final class CachedRegionTracker implements Cache.Listener { Region removedRegion = new Region(span.position, span.position + span.length); // Look up a region this span falls into. - Region floorRegion = regions.floor(removedRegion); + @Nullable Region floorRegion = regions.floor(removedRegion); if (floorRegion == null) { Log.e(TAG, "Removed a span we were not aware of"); return; @@ -134,8 +135,8 @@ public final class CachedRegionTracker implements Cache.Listener { private void mergeSpan(CacheSpan span) { Region newRegion = new Region(span.position, span.position + span.length); - Region floorRegion = regions.floor(newRegion); - Region ceilingRegion = regions.ceiling(newRegion); + @Nullable Region floorRegion = regions.floor(newRegion); + @Nullable Region ceilingRegion = regions.ceiling(newRegion); boolean floorConnects = regionsConnect(floorRegion, newRegion); boolean ceilingConnects = regionsConnect(newRegion, ceilingRegion); @@ -168,7 +169,7 @@ public final class CachedRegionTracker implements Cache.Listener { } } - private boolean regionsConnect(Region lower, Region upper) { + private boolean regionsConnect(@Nullable Region lower, @Nullable Region upper) { return lower != null && upper != null && lower.endOffset == upper.startOffset; } diff --git a/library/core/src/main/java/com/google/android/exoplayer2/upstream/cache/ContentMetadata.java b/library/core/src/main/java/com/google/android/exoplayer2/upstream/cache/ContentMetadata.java index 4cc6e6b860..26b6d83a43 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/upstream/cache/ContentMetadata.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/upstream/cache/ContentMetadata.java @@ -81,7 +81,7 @@ public interface ContentMetadata { */ @Nullable static Uri getRedirectedUri(ContentMetadata contentMetadata) { - String redirectedUri = contentMetadata.get(KEY_REDIRECTED_URI, (String) null); + @Nullable String redirectedUri = contentMetadata.get(KEY_REDIRECTED_URI, (String) null); return redirectedUri == null ? null : Uri.parse(redirectedUri); } } diff --git a/library/core/src/main/java/com/google/android/exoplayer2/upstream/cache/ContentMetadataMutations.java b/library/core/src/main/java/com/google/android/exoplayer2/upstream/cache/ContentMetadataMutations.java index 5715b8fbd4..f6cac58997 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/upstream/cache/ContentMetadataMutations.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/upstream/cache/ContentMetadataMutations.java @@ -73,8 +73,7 @@ public class ContentMetadataMutations { } /** - * Adds a mutation to set a metadata value. Passing {@code null} as {@code name} or {@code value} - * isn't allowed. + * Adds a mutation to set a metadata value. * * @param name The name of the metadata value. * @param value The value to be set. @@ -85,7 +84,7 @@ public class ContentMetadataMutations { } /** - * Adds a mutation to set a metadata value. Passing {@code null} as {@code name} isn't allowed. + * Adds a mutation to set a metadata value. * * @param name The name of the metadata value. * @param value The value to be set. @@ -96,8 +95,7 @@ public class ContentMetadataMutations { } /** - * Adds a mutation to set a metadata value. Passing {@code null} as {@code name} or {@code value} - * isn't allowed. + * Adds a mutation to set a metadata value. * * @param name The name of the metadata value. * @param value The value to be set. diff --git a/library/core/src/main/java/com/google/android/exoplayer2/upstream/cache/DefaultContentMetadata.java b/library/core/src/main/java/com/google/android/exoplayer2/upstream/cache/DefaultContentMetadata.java index 1f07af938a..c3f06252e4 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/upstream/cache/DefaultContentMetadata.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/upstream/cache/DefaultContentMetadata.java @@ -67,8 +67,8 @@ public final class DefaultContentMetadata implements ContentMetadata { @Override @Nullable public final byte[] get(String name, @Nullable byte[] defaultValue) { - if (metadata.containsKey(name)) { - byte[] bytes = metadata.get(name); + @Nullable byte[] bytes = metadata.get(name); + if (bytes != null) { return Arrays.copyOf(bytes, bytes.length); } else { return defaultValue; @@ -78,8 +78,8 @@ public final class DefaultContentMetadata implements ContentMetadata { @Override @Nullable public final String get(String name, @Nullable String defaultValue) { - if (metadata.containsKey(name)) { - byte[] bytes = metadata.get(name); + @Nullable byte[] bytes = metadata.get(name); + if (bytes != null) { return new String(bytes, Charset.forName(C.UTF8_NAME)); } else { return defaultValue; @@ -88,8 +88,8 @@ public final class DefaultContentMetadata implements ContentMetadata { @Override public final long get(String name, long defaultValue) { - if (metadata.containsKey(name)) { - byte[] bytes = metadata.get(name); + @Nullable byte[] bytes = metadata.get(name); + if (bytes != null) { return ByteBuffer.wrap(bytes).getLong(); } else { return defaultValue; @@ -130,7 +130,7 @@ public final class DefaultContentMetadata implements ContentMetadata { } for (Entry entry : first.entrySet()) { byte[] value = entry.getValue(); - byte[] otherValue = second.get(entry.getKey()); + @Nullable byte[] otherValue = second.get(entry.getKey()); if (!Arrays.equals(value, otherValue)) { return false; } @@ -153,8 +153,8 @@ public final class DefaultContentMetadata implements ContentMetadata { } private static void addValues(HashMap metadata, Map values) { - for (String name : values.keySet()) { - metadata.put(name, getBytes(values.get(name))); + for (Entry entry : values.entrySet()) { + metadata.put(entry.getKey(), getBytes(entry.getValue())); } } diff --git a/library/core/src/main/java/com/google/android/exoplayer2/upstream/cache/SimpleCache.java b/library/core/src/main/java/com/google/android/exoplayer2/upstream/cache/SimpleCache.java index a4fade25e0..5f420c3197 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/upstream/cache/SimpleCache.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/upstream/cache/SimpleCache.java @@ -465,8 +465,7 @@ public final class SimpleCache implements Cache { @Override public synchronized void releaseHoleSpan(CacheSpan holeSpan) { Assertions.checkState(!released); - CachedContent cachedContent = contentIndex.get(holeSpan.key); - Assertions.checkNotNull(cachedContent); + CachedContent cachedContent = Assertions.checkNotNull(contentIndex.get(holeSpan.key)); Assertions.checkState(cachedContent.isLocked()); cachedContent.setLocked(false); contentIndex.maybeRemove(cachedContent.key); @@ -482,14 +481,14 @@ public final class SimpleCache implements Cache { @Override public synchronized boolean isCached(String key, long position, long length) { Assertions.checkState(!released); - CachedContent cachedContent = contentIndex.get(key); + @Nullable CachedContent cachedContent = contentIndex.get(key); return cachedContent != null && cachedContent.getCachedBytesLength(position, length) >= length; } @Override public synchronized long getCachedLength(String key, long position, long length) { Assertions.checkState(!released); - CachedContent cachedContent = contentIndex.get(key); + @Nullable CachedContent cachedContent = contentIndex.get(key); return cachedContent != null ? cachedContent.getCachedBytesLength(position, length) : -length; } @@ -524,7 +523,7 @@ public final class SimpleCache implements Cache { } } - File[] files = cacheDir.listFiles(); + @Nullable File[] files = cacheDir.listFiles(); if (files == null) { String message = "Failed to list cache directory files: " + cacheDir; Log.e(TAG, message); @@ -605,11 +604,13 @@ public final class SimpleCache implements Cache { } long length = C.LENGTH_UNSET; long lastTouchTimestamp = C.TIME_UNSET; + @Nullable CacheFileMetadata metadata = fileMetadata != null ? fileMetadata.remove(fileName) : null; if (metadata != null) { length = metadata.length; lastTouchTimestamp = metadata.lastTouchTimestamp; } + @Nullable SimpleCacheSpan span = SimpleCacheSpan.createCacheEntry(file, length, lastTouchTimestamp, contentIndex); if (span != null) { @@ -666,7 +667,7 @@ public final class SimpleCache implements Cache { * @return The corresponding cache {@link SimpleCacheSpan}. */ private SimpleCacheSpan getSpan(String key, long position) { - CachedContent cachedContent = contentIndex.get(key); + @Nullable CachedContent cachedContent = contentIndex.get(key); if (cachedContent == null) { return SimpleCacheSpan.createOpenHole(key, position); } @@ -694,7 +695,7 @@ public final class SimpleCache implements Cache { } private void removeSpanInternal(CacheSpan span) { - CachedContent cachedContent = contentIndex.get(span.key); + @Nullable CachedContent cachedContent = contentIndex.get(span.key); if (cachedContent == null || !cachedContent.removeSpan(span)) { return; } @@ -732,7 +733,7 @@ public final class SimpleCache implements Cache { } private void notifySpanRemoved(CacheSpan span) { - ArrayList keyListeners = listeners.get(span.key); + @Nullable ArrayList keyListeners = listeners.get(span.key); if (keyListeners != null) { for (int i = keyListeners.size() - 1; i >= 0; i--) { keyListeners.get(i).onSpanRemoved(this, span); @@ -742,7 +743,7 @@ public final class SimpleCache implements Cache { } private void notifySpanAdded(SimpleCacheSpan span) { - ArrayList keyListeners = listeners.get(span.key); + @Nullable ArrayList keyListeners = listeners.get(span.key); if (keyListeners != null) { for (int i = keyListeners.size() - 1; i >= 0; i--) { keyListeners.get(i).onSpanAdded(this, span); @@ -752,7 +753,7 @@ public final class SimpleCache implements Cache { } private void notifySpanTouched(SimpleCacheSpan oldSpan, CacheSpan newSpan) { - ArrayList keyListeners = listeners.get(oldSpan.key); + @Nullable ArrayList keyListeners = listeners.get(oldSpan.key); if (keyListeners != null) { for (int i = keyListeners.size() - 1; i >= 0; i--) { keyListeners.get(i).onSpanTouched(this, oldSpan, newSpan); diff --git a/library/core/src/main/java/com/google/android/exoplayer2/upstream/cache/SimpleCacheSpan.java b/library/core/src/main/java/com/google/android/exoplayer2/upstream/cache/SimpleCacheSpan.java index 5f6ea338e6..ce195baf4e 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/upstream/cache/SimpleCacheSpan.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/upstream/cache/SimpleCacheSpan.java @@ -91,6 +91,7 @@ import java.util.regex.Pattern; * @param length The length of the cache file in bytes, or {@link C#LENGTH_UNSET} to query the * underlying file system. Querying the underlying file system can be expensive, so callers * that already know the length of the file should pass it explicitly. + * @param index The cached content index. * @return The span, or null if the file name is not correctly formatted, or if the id is not * present in the content index, or if the length is 0. */ @@ -108,6 +109,7 @@ import java.util.regex.Pattern; * that already know the length of the file should pass it explicitly. * @param lastTouchTimestamp The last touch timestamp, or {@link C#TIME_UNSET} to use the file * timestamp. + * @param index The cached content index. * @return The span, or null if the file name is not correctly formatted, or if the id is not * present in the content index, or if the length is 0. */ @@ -130,7 +132,7 @@ import java.util.regex.Pattern; } int id = Integer.parseInt(matcher.group(1)); - String key = index.getKeyForId(id); + @Nullable String key = index.getKeyForId(id); if (key == null) { return null; } @@ -153,26 +155,26 @@ import java.util.regex.Pattern; * Upgrades the cache file if it is created by an earlier version of {@link SimpleCache}. * * @param file The cache file. - * @param index Cached content index. + * @param index The cached content index. * @return Upgraded cache file or {@code null} if the file name is not correctly formatted or the * file can not be renamed. */ @Nullable private static File upgradeFile(File file, CachedContentIndex index) { - String key; + @Nullable String key = null; String filename = file.getName(); Matcher matcher = CACHE_FILE_PATTERN_V2.matcher(filename); if (matcher.matches()) { key = Util.unescapeFileName(matcher.group(1)); - if (key == null) { - return null; - } } else { matcher = CACHE_FILE_PATTERN_V1.matcher(filename); - if (!matcher.matches()) { - return null; + if (matcher.matches()) { + key = matcher.group(1); // Keys were not escaped in version 1. } - key = matcher.group(1); // Keys were not escaped in version 1. + } + + if (key == null) { + return null; } File newCacheFile = diff --git a/library/core/src/main/java/com/google/android/exoplayer2/upstream/cache/package-info.java b/library/core/src/main/java/com/google/android/exoplayer2/upstream/cache/package-info.java new file mode 100644 index 0000000000..bb6cf77458 --- /dev/null +++ b/library/core/src/main/java/com/google/android/exoplayer2/upstream/cache/package-info.java @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +@NonNullApi +package com.google.android.exoplayer2.upstream.cache; + +import com.google.android.exoplayer2.util.NonNullApi; diff --git a/library/core/src/main/java/com/google/android/exoplayer2/upstream/crypto/package-info.java b/library/core/src/main/java/com/google/android/exoplayer2/upstream/crypto/package-info.java new file mode 100644 index 0000000000..9c4005e815 --- /dev/null +++ b/library/core/src/main/java/com/google/android/exoplayer2/upstream/crypto/package-info.java @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +@NonNullApi +package com.google.android.exoplayer2.upstream.crypto; + +import com.google.android.exoplayer2.util.NonNullApi;