diff --git a/library/core/src/androidTest/java/com/google/android/exoplayer2/upstream/cache/CachedContentIndexTest.java b/library/core/src/androidTest/java/com/google/android/exoplayer2/upstream/cache/CachedContentIndexTest.java index b6c67d32a1..58531346ab 100644 --- a/library/core/src/androidTest/java/com/google/android/exoplayer2/upstream/cache/CachedContentIndexTest.java +++ b/library/core/src/androidTest/java/com/google/android/exoplayer2/upstream/cache/CachedContentIndexTest.java @@ -1,8 +1,24 @@ +/* + * Copyright (C) 2018 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. + */ package com.google.android.exoplayer2.upstream.cache; import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertWithMessage; +import android.net.Uri; import android.test.InstrumentationTestCase; import android.util.SparseArray; import com.google.android.exoplayer2.C; @@ -34,19 +50,22 @@ public class CachedContentIndexTest extends InstrumentationTestCase { 0, 0, 0, 2, // version 0, 0, 0, 0, // flags 0, 0, 0, 2, // number_of_CachedContent - 0, 0, 0, 1, // cache_id + 0, 0, 0, 5, // cache_id 5 0, 5, 65, 66, 67, 68, 69, // cache_key "ABCDE" - 0, 0, 0, 1, // metadata count + 0, 0, 0, 2, // metadata count + 0, 9, 101, 120, 111, 95, 114, 101, 100, 105, 114, // "exo_redir" + 0, 0, 0, 5, // value length + 97, 98, 99, 100, 101, // Redirected Uri "abcde" 0, 7, 101, 120, 111, 95, 108, 101, 110, // "exo_len" 0, 0, 0, 8, // value length 0, 0, 0, 0, 0, 0, 0, 10, // original_content_length - 0, 0, 0, 0, // cache_id + 0, 0, 0, 2, // cache_id 2 0, 5, 75, 76, 77, 78, 79, // cache_key "KLMNO" 0, 0, 0, 1, // metadata count 0, 7, 101, 120, 111, 95, 108, 101, 110, // "exo_len" 0, 0, 0, 8, // value length 0, 0, 0, 0, 0, 0, 10, 0, // original_content_length - (byte) 0x42, (byte) 0x4A, (byte) 0x4F, (byte) 0x6F // hashcode_of_CachedContent_array + 0x12, 0x15, 0x66, (byte) 0x8A // hashcode_of_CachedContent_array }; private CachedContentIndex index; private File cacheDir; @@ -124,10 +143,14 @@ public class CachedContentIndexTest extends InstrumentationTestCase { index.load(); assertThat(index.getAll()).hasSize(2); + assertThat(index.assignIdForKey("ABCDE")).isEqualTo(5); - assertThat(index.getContentLength("ABCDE")).isEqualTo(10); + ContentMetadata metadata = index.get("ABCDE").getMetadata(); + assertThat(ContentMetadataInternal.getContentLength(metadata)).isEqualTo(10); + assertThat(index.assignIdForKey("KLMNO")).isEqualTo(2); - assertThat(index.getContentLength("KLMNO")).isEqualTo(2560); + ContentMetadata metadata2 = index.get("KLMNO").getMetadata(); + assertThat(ContentMetadataInternal.getContentLength(metadata2)).isEqualTo(2560); } public void testLoadV2() throws Exception { @@ -137,26 +160,15 @@ public class CachedContentIndexTest extends InstrumentationTestCase { index.load(); assertThat(index.getAll()).hasSize(2); - assertThat(index.assignIdForKey("ABCDE")).isEqualTo(1); - assertThat(index.getContentLength("ABCDE")).isEqualTo(10); - assertThat(index.assignIdForKey("KLMNO")).isEqualTo(0); - assertThat(index.getContentLength("KLMNO")).isEqualTo(2560); - } - public void testStore() throws Exception { - index.getOrAdd("KLMNO"); - index.setContentLength("KLMNO", 2560); - index.getOrAdd("ABCDE"); - index.setContentLength("ABCDE", 10); + assertThat(index.assignIdForKey("ABCDE")).isEqualTo(5); + ContentMetadata metadata = index.get("ABCDE").getMetadata(); + assertThat(ContentMetadataInternal.getContentLength(metadata)).isEqualTo(10); + assertThat(ContentMetadataInternal.getRedirectedUri(metadata)).isEqualTo(Uri.parse("abcde")); - index.store(); - - FileInputStream fos = new FileInputStream(new File(cacheDir, CachedContentIndex.FILE_NAME)); - byte[] buffer = Util.toByteArray(fos); - - // TODO: The order of the CachedContent stored in index file isn't defined so this test may fail - // on a different implementation of the underlying set - assertThat(buffer).isEqualTo(testIndexV2File); + assertThat(index.assignIdForKey("KLMNO")).isEqualTo(2); + ContentMetadata metadata2 = index.get("KLMNO").getMetadata(); + assertThat(ContentMetadataInternal.getContentLength(metadata2)).isEqualTo(2560); } public void testAssignIdForKeyAndGetKeyForId() throws Exception { @@ -171,13 +183,6 @@ public class CachedContentIndexTest extends InstrumentationTestCase { assertThat(index.assignIdForKey(key2)).isEqualTo(id2); } - public void testSetGetContentLength() throws Exception { - final String key1 = "key1"; - assertThat(index.getContentLength(key1)).isEqualTo(C.LENGTH_UNSET); - index.setContentLength(key1, 10); - assertThat(index.getContentLength(key1)).isEqualTo(10); - } - public void testGetNewId() throws Exception { SparseArray idToKey = new SparseArray<>(); assertThat(CachedContentIndex.getNewId(idToKey)).isEqualTo(0); @@ -276,8 +281,13 @@ public class CachedContentIndexTest extends InstrumentationTestCase { private void assertStoredAndLoadedEqual(CachedContentIndex index, CachedContentIndex index2) throws IOException { - index.getOrAdd("key1"); - index.getOrAdd("key2"); + ContentMetadataMutations mutations1 = new ContentMetadataMutations(); + ContentMetadataInternal.setContentLength(mutations1, 2560); + index.getOrAdd("KLMNO").applyMetadataMutations(mutations1); + ContentMetadataMutations mutations2 = new ContentMetadataMutations(); + ContentMetadataInternal.setContentLength(mutations2, 10); + ContentMetadataInternal.setRedirectedUri(mutations2, Uri.parse("abcde")); + index.getOrAdd("ABCDE").applyMetadataMutations(mutations2); index.store(); index2.load(); 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 53898a6c87..6de48cb9d2 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 @@ -422,9 +422,9 @@ public final class CacheDataSource implements DataSource { ContentMetadataMutations mutations = new ContentMetadataMutations(); boolean isRedirected = !uri.equals(actualUri); if (isRedirected) { - mutations.set(ContentMetadata.METADATA_NAME_REDIRECTED_URI, actualUri.toString()); + ContentMetadataInternal.setRedirectedUri(mutations, actualUri); } else { - mutations.remove(ContentMetadata.METADATA_NAME_REDIRECTED_URI); + ContentMetadataInternal.removeRedirectedUri(mutations); } try { cache.applyContentMetadataMutations(key, mutations); @@ -437,9 +437,9 @@ public final class CacheDataSource implements DataSource { } private static Uri loadRedirectedUriOrReturnGivenUri(Cache cache, String key, Uri uri) { - ContentMetadata metadata = cache.getContentMetadata(key); - String redirection = metadata.get(ContentMetadata.METADATA_NAME_REDIRECTED_URI, (String) null); - return redirection == null ? uri : Uri.parse(redirection); + ContentMetadata contentMetadata = cache.getContentMetadata(key); + Uri redirectedUri = ContentMetadataInternal.getRedirectedUri(contentMetadata); + return redirectedUri == null ? uri : redirectedUri; } private static boolean isCausedByPositionOutOfRange(IOException e) { 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 bc17609739..7b0b459dd9 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 @@ -15,7 +15,6 @@ */ package com.google.android.exoplayer2.upstream.cache; -import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.upstream.cache.Cache.CacheException; import com.google.android.exoplayer2.util.Assertions; import java.io.DataInputStream; @@ -56,7 +55,9 @@ import java.util.TreeSet; CachedContent cachedContent = new CachedContent(id, key); if (version < VERSION_METADATA_INTRODUCED) { long length = input.readLong(); - cachedContent.setLength(length); + ContentMetadataMutations mutations = new ContentMetadataMutations(); + ContentMetadataInternal.setContentLength(mutations, length); + cachedContent.applyMetadataMutations(mutations); } else { cachedContent.metadata = DefaultContentMetadata.readFromStream(input); } @@ -101,20 +102,7 @@ import java.util.TreeSet; public boolean applyMetadataMutations(ContentMetadataMutations mutations) { DefaultContentMetadata oldMetadata = metadata; metadata = metadata.copyWithMutationsApplied(mutations); - return metadata.equals(oldMetadata); - } - - /** - * Returns the length of the original stream, or {@link C#LENGTH_UNSET} if the length is unknown. - */ - public long getLength() { - return metadata.get(ContentMetadata.METADATA_NAME_LENGTH, C.LENGTH_UNSET); - } - - /** Sets the length of the content. */ - public void setLength(long length) { - applyMetadataMutations( - new ContentMetadataMutations().set(ContentMetadata.METADATA_NAME_LENGTH, length)); + return !metadata.equals(oldMetadata); } /** Returns whether the content is locked. */ @@ -232,7 +220,7 @@ import java.util.TreeSet; int result = id; result = 31 * result + key.hashCode(); if (version < VERSION_METADATA_INTRODUCED) { - long length = getLength(); + long length = ContentMetadataInternal.getContentLength(metadata); result = 31 * result + (int) (length ^ (length >>> 32)); } else { result = 31 * result + metadata.hashCode(); 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 c1e8f4b223..7b5fd2c598 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 @@ -17,7 +17,6 @@ package com.google.android.exoplayer2.upstream.cache; import android.util.Log; import android.util.SparseArray; -import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.upstream.cache.Cache.CacheException; import com.google.android.exoplayer2.util.Assertions; import com.google.android.exoplayer2.util.AtomicFile; @@ -197,27 +196,6 @@ import javax.crypto.spec.SecretKeySpec; return keyToContent.keySet(); } - /** - * Sets the content length for the given key. A new {@link CachedContent} is added if there isn't - * one already with the given key. - */ - public void setContentLength(String key, long length) { - CachedContent cachedContent = getOrAdd(key); - if (cachedContent.getLength() != length) { - cachedContent.setLength(length); - changed = true; - } - } - - /** - * Returns the content length for the given key if one set, or {@link - * com.google.android.exoplayer2.C#LENGTH_UNSET} otherwise. - */ - public long getContentLength(String key) { - CachedContent cachedContent = get(key); - return cachedContent == null ? C.LENGTH_UNSET : cachedContent.getLength(); - } - /** * Applies {@code mutations} to the {@link ContentMetadata} for the given key. A new {@link * CachedContent} is added if there isn't one already with the given key. 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 fba073451f..aacd11f915 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 @@ -25,10 +25,6 @@ public interface ContentMetadata { /** Prefix of internal metadata names. */ String INTERNAL_METADATA_NAME_PREFIX = "exo_"; - /** Name of internal metadata to hold redirected URI. */ - String METADATA_NAME_REDIRECTED_URI = INTERNAL_METADATA_NAME_PREFIX + "redir"; - /** Name of internal metadata to hold content length. */ - String METADATA_NAME_LENGTH = INTERNAL_METADATA_NAME_PREFIX + "len"; /** * Returns a metadata value. 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 new file mode 100644 index 0000000000..3376dd6944 --- /dev/null +++ b/library/core/src/main/java/com/google/android/exoplayer2/upstream/cache/ContentMetadataInternal.java @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2018 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. + */ +package com.google.android.exoplayer2.upstream.cache; + +import android.net.Uri; +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 { + + private static final String PREFIX = ContentMetadata.INTERNAL_METADATA_NAME_PREFIX; + private static final String METADATA_NAME_REDIRECTED_URI = PREFIX + "redir"; + private static final String METADATA_NAME_CONTENT_LENGTH = PREFIX + "len"; + + /** Returns the content length metadata, or {@link C#LENGTH_UNSET} if not set. */ + public static long getContentLength(ContentMetadata contentMetadata) { + return contentMetadata.get(METADATA_NAME_CONTENT_LENGTH, C.LENGTH_UNSET); + } + + /** Adds a mutation to set content length metadata value. */ + public static void setContentLength(ContentMetadataMutations mutations, long length) { + mutations.set(METADATA_NAME_CONTENT_LENGTH, length); + } + + /** Adds a mutation to remove content length metadata value. */ + public static void removeContentLength(ContentMetadataMutations mutations) { + mutations.remove(METADATA_NAME_CONTENT_LENGTH); + } + + /** Returns the redirected uri metadata, or {@code null} if not set. */ + public @Nullable static Uri getRedirectedUri(ContentMetadata contentMetadata) { + String redirectedUri = contentMetadata.get(METADATA_NAME_REDIRECTED_URI, (String) null); + return redirectedUri == null ? null : Uri.parse(redirectedUri); + } + + /** + * Adds a mutation to set redirected uri metadata value. Passing {@code null} as {@code uri} isn't + * allowed. + */ + public static void setRedirectedUri(ContentMetadataMutations mutations, Uri uri) { + mutations.set(METADATA_NAME_REDIRECTED_URI, uri.toString()); + } + + /** Adds a mutation to remove redirected uri metadata value. */ + public static void removeRedirectedUri(ContentMetadataMutations mutations) { + mutations.remove(METADATA_NAME_REDIRECTED_URI); + } +} 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 57af765926..a5869122fe 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 @@ -256,7 +256,7 @@ public final class SimpleCache implements Cache { return; } // Check if the span conflicts with the set content length - long length = cachedContent.getLength(); + long length = ContentMetadataInternal.getContentLength(cachedContent.getMetadata()); if (length != C.LENGTH_UNSET) { Assertions.checkState((span.position + span.length) <= length); } @@ -298,15 +298,14 @@ public final class SimpleCache implements Cache { @Override public synchronized void setContentLength(String key, long length) throws CacheException { - Assertions.checkState(!released); - index.setContentLength(key, length); - index.store(); + ContentMetadataMutations mutations = new ContentMetadataMutations(); + ContentMetadataInternal.setContentLength(mutations, length); + applyContentMetadataMutations(key, mutations); } @Override public synchronized long getContentLength(String key) { - Assertions.checkState(!released); - return index.getContentLength(key); + return ContentMetadataInternal.getContentLength(getContentMetadata(key)); } @Override