diff --git a/library/core/src/main/java/com/google/android/exoplayer2/upstream/cache/CachedContent.java b/library/core/src/main/java/com/google/android/exoplayer2/upstream/cache/CachedContent.java index 65abcb4059..4c3f58f2c6 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/upstream/cache/CachedContent.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/upstream/cache/CachedContent.java @@ -16,6 +16,7 @@ package com.google.android.exoplayer2.upstream.cache; import static com.google.android.exoplayer2.util.Assertions.checkArgument; +import static com.google.android.exoplayer2.util.Assertions.checkNotNull; import static com.google.android.exoplayer2.util.Assertions.checkState; import static java.lang.Math.max; import static java.lang.Math.min; @@ -219,9 +220,9 @@ import java.util.TreeSet; public SimpleCacheSpan setLastTouchTimestamp( SimpleCacheSpan cacheSpan, long lastTouchTimestamp, boolean updateFile) { checkState(cachedSpans.remove(cacheSpan)); - File file = cacheSpan.file; + File file = checkNotNull(cacheSpan.file); if (updateFile) { - File directory = file.getParentFile(); + File directory = checkNotNull(file.getParentFile()); long position = cacheSpan.position; File newFile = SimpleCacheSpan.getCacheFile(directory, id, position, lastTouchTimestamp); if (file.renameTo(newFile)) { @@ -244,7 +245,9 @@ import java.util.TreeSet; /** Removes the given span from cache. */ public boolean removeSpan(CacheSpan span) { if (cachedSpans.remove(span)) { - span.file.delete(); + if (span.file != null) { + span.file.delete(); + } return true; } return false; 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 452abca7c2..850ac59f04 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 @@ -15,6 +15,9 @@ */ package com.google.android.exoplayer2.upstream.cache; +import static com.google.android.exoplayer2.util.Assertions.checkNotNull; +import static com.google.android.exoplayer2.util.Assertions.checkState; +import static com.google.android.exoplayer2.util.Util.castNonNull; import static java.lang.Math.min; import android.annotation.SuppressLint; @@ -61,6 +64,7 @@ import javax.crypto.NoSuchPaddingException; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; import org.checkerframework.checker.nullness.compatqual.NullableType; +import org.checkerframework.checker.nullness.qual.MonotonicNonNull; /** Maintains the index of cached content. */ /* package */ class CachedContentIndex { @@ -155,13 +159,15 @@ import org.checkerframework.checker.nullness.compatqual.NullableType; @Nullable byte[] legacyStorageSecretKey, boolean legacyStorageEncrypt, boolean preferLegacyStorage) { - Assertions.checkState(databaseProvider != null || legacyStorageDir != null); + checkState(databaseProvider != null || legacyStorageDir != null); keyToContent = new HashMap<>(); idToKey = new SparseArray<>(); removedIds = new SparseBooleanArray(); newIds = new SparseBooleanArray(); + @Nullable Storage databaseStorage = databaseProvider != null ? new DatabaseStorage(databaseProvider) : null; + @Nullable Storage legacyStorage = legacyStorageDir != null ? new LegacyStorage( @@ -170,7 +176,7 @@ import org.checkerframework.checker.nullness.compatqual.NullableType; legacyStorageEncrypt) : null; if (databaseStorage == null || (legacyStorage != null && preferLegacyStorage)) { - storage = legacyStorage; + storage = castNonNull(legacyStorage); previousStorage = databaseStorage; } else { storage = databaseStorage; @@ -325,7 +331,7 @@ import org.checkerframework.checker.nullness.compatqual.NullableType; /** Returns a {@link ContentMetadata} for the given key. */ public ContentMetadata getContentMetadata(String key) { - CachedContent cachedContent = get(key); + @Nullable CachedContent cachedContent = get(key); return cachedContent != null ? cachedContent.getMetadata() : DefaultContentMetadata.EMPTY; } @@ -358,7 +364,7 @@ import org.checkerframework.checker.nullness.compatqual.NullableType; * returns the smallest unused non-negative integer. */ @VisibleForTesting - /* package */ static int getNewId(SparseArray idToKey) { + /* package */ static int getNewId(SparseArray<@NullableType String> idToKey) { int size = idToKey.size(); int id = size == 0 ? 0 : (idToKey.keyAt(size - 1) + 1); if (id < 0) { // In case if we pass max int value. @@ -512,8 +518,9 @@ import org.checkerframework.checker.nullness.compatqual.NullableType; @Nullable private ReusableBufferedOutputStream bufferedOutputStream; public LegacyStorage(File file, @Nullable byte[] secretKey, boolean encrypt) { - Cipher cipher = null; - SecretKeySpec secretKeySpec = null; + checkState(secretKey != null || !encrypt); + @Nullable Cipher cipher = null; + @Nullable SecretKeySpec secretKeySpec = null; if (secretKey != null) { Assertions.checkArgument(secretKey.length == 16); try { @@ -550,7 +557,7 @@ import org.checkerframework.checker.nullness.compatqual.NullableType; @Override public void load( HashMap content, SparseArray<@NullableType String> idToKey) { - Assertions.checkState(!changed); + checkState(!changed); if (!readFile(content, idToKey)) { content.clear(); idToKey.clear(); @@ -588,7 +595,7 @@ import org.checkerframework.checker.nullness.compatqual.NullableType; return true; } - DataInputStream input = null; + @Nullable DataInputStream input = null; try { InputStream inputStream = new BufferedInputStream(atomicFile.openRead()); input = new DataInputStream(inputStream); @@ -606,7 +613,7 @@ import org.checkerframework.checker.nullness.compatqual.NullableType; input.readFully(initializationVector); IvParameterSpec ivParameterSpec = new IvParameterSpec(initializationVector); try { - cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, ivParameterSpec); + cipher.init(Cipher.DECRYPT_MODE, castNonNull(secretKeySpec), ivParameterSpec); } catch (InvalidKeyException | InvalidAlgorithmParameterException e) { throw new IllegalStateException(e); } @@ -647,6 +654,7 @@ import org.checkerframework.checker.nullness.compatqual.NullableType; } else { bufferedOutputStream.reset(outputStream); } + ReusableBufferedOutputStream bufferedOutputStream = this.bufferedOutputStream; output = new DataOutputStream(bufferedOutputStream); output.writeInt(VERSION); @@ -655,11 +663,12 @@ import org.checkerframework.checker.nullness.compatqual.NullableType; if (encrypt) { byte[] initializationVector = new byte[16]; - random.nextBytes(initializationVector); + castNonNull(random).nextBytes(initializationVector); output.write(initializationVector); IvParameterSpec ivParameterSpec = new IvParameterSpec(initializationVector); try { - cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivParameterSpec); + castNonNull(cipher) + .init(Cipher.ENCRYPT_MODE, castNonNull(secretKeySpec), ivParameterSpec); } catch (InvalidKeyException | InvalidAlgorithmParameterException e) { throw new IllegalStateException(e); // Should never happen. } @@ -762,16 +771,17 @@ import org.checkerframework.checker.nullness.compatqual.NullableType; + " BLOB NOT NULL)"; private final DatabaseProvider databaseProvider; - private final SparseArray pendingUpdates; + private final SparseArray<@NullableType CachedContent> pendingUpdates; - private String hexUid; - private String tableName; + private @MonotonicNonNull String hexUid; + private @MonotonicNonNull String tableName; public static void delete(DatabaseProvider databaseProvider, long uid) throws DatabaseIOException { delete(databaseProvider, Long.toHexString(uid)); } + @SuppressWarnings("nullness:initialization.fields.uninitialized") public DatabaseStorage(DatabaseProvider databaseProvider) { this.databaseProvider = databaseProvider; pendingUpdates = new SparseArray<>(); @@ -788,26 +798,26 @@ import org.checkerframework.checker.nullness.compatqual.NullableType; return VersionTable.getVersion( databaseProvider.getReadableDatabase(), VersionTable.FEATURE_CACHE_CONTENT_METADATA, - hexUid) + checkNotNull(hexUid)) != VersionTable.VERSION_UNSET; } @Override public void delete() throws DatabaseIOException { - delete(databaseProvider, hexUid); + delete(databaseProvider, checkNotNull(hexUid)); } @Override public void load( HashMap content, SparseArray<@NullableType String> idToKey) throws IOException { - Assertions.checkState(pendingUpdates.size() == 0); + checkState(pendingUpdates.size() == 0); try { int version = VersionTable.getVersion( databaseProvider.getReadableDatabase(), VersionTable.FEATURE_CACHE_CONTENT_METADATA, - hexUid); + checkNotNull(hexUid)); if (version != TABLE_VERSION) { SQLiteDatabase writableDatabase = databaseProvider.getWritableDatabase(); writableDatabase.beginTransactionNonExclusive(); @@ -871,7 +881,7 @@ import org.checkerframework.checker.nullness.compatqual.NullableType; writableDatabase.beginTransactionNonExclusive(); try { for (int i = 0; i < pendingUpdates.size(); i++) { - CachedContent cachedContent = pendingUpdates.valueAt(i); + @Nullable CachedContent cachedContent = pendingUpdates.valueAt(i); if (cachedContent == null) { deleteRow(writableDatabase, pendingUpdates.keyAt(i)); } else { @@ -906,7 +916,7 @@ import org.checkerframework.checker.nullness.compatqual.NullableType; return databaseProvider .getReadableDatabase() .query( - tableName, + checkNotNull(tableName), COLUMNS, /* selection= */ null, /* selectionArgs= */ null, @@ -917,13 +927,17 @@ import org.checkerframework.checker.nullness.compatqual.NullableType; private void initializeTable(SQLiteDatabase writableDatabase) throws DatabaseIOException { VersionTable.setVersion( - writableDatabase, VersionTable.FEATURE_CACHE_CONTENT_METADATA, hexUid, TABLE_VERSION); - dropTable(writableDatabase, tableName); + writableDatabase, + VersionTable.FEATURE_CACHE_CONTENT_METADATA, + checkNotNull(hexUid), + TABLE_VERSION); + dropTable(writableDatabase, checkNotNull(tableName)); writableDatabase.execSQL("CREATE TABLE " + tableName + " " + TABLE_SCHEMA); } private void deleteRow(SQLiteDatabase writableDatabase, int key) { - writableDatabase.delete(tableName, WHERE_ID_EQUALS, new String[] {Integer.toString(key)}); + writableDatabase.delete( + checkNotNull(tableName), WHERE_ID_EQUALS, new String[] {Integer.toString(key)}); } private void addOrUpdateRow(SQLiteDatabase writableDatabase, CachedContent cachedContent) @@ -936,7 +950,7 @@ import org.checkerframework.checker.nullness.compatqual.NullableType; values.put(COLUMN_ID, cachedContent.id); values.put(COLUMN_KEY, cachedContent.key); values.put(COLUMN_METADATA, data); - writableDatabase.replaceOrThrow(tableName, /* nullColumnHack= */ null, values); + writableDatabase.replaceOrThrow(checkNotNull(tableName), /* nullColumnHack= */ null, values); } private static void delete(DatabaseProvider databaseProvider, String hexUid)