commit
						b51ca3a025
					
				@ -1,940 +0,0 @@ | 
				
			||||
package io.legado.app.help.exoplayer; | 
				
			||||
 | 
				
			||||
/* | 
				
			||||
 * Copyright (C) 2016 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. | 
				
			||||
 */ | 
				
			||||
 | 
				
			||||
import static com.google.android.exoplayer2.util.Assertions.checkNotNull; | 
				
			||||
import static com.google.android.exoplayer2.util.Util.castNonNull; | 
				
			||||
import static java.lang.Math.min; | 
				
			||||
import static java.lang.annotation.ElementType.FIELD; | 
				
			||||
import static java.lang.annotation.ElementType.LOCAL_VARIABLE; | 
				
			||||
import static java.lang.annotation.ElementType.METHOD; | 
				
			||||
import static java.lang.annotation.ElementType.PARAMETER; | 
				
			||||
import static java.lang.annotation.ElementType.TYPE_USE; | 
				
			||||
 | 
				
			||||
import android.net.Uri; | 
				
			||||
 | 
				
			||||
import androidx.annotation.IntDef; | 
				
			||||
import androidx.annotation.NonNull; | 
				
			||||
import androidx.annotation.Nullable; | 
				
			||||
 | 
				
			||||
import com.google.android.exoplayer2.C; | 
				
			||||
import com.google.android.exoplayer2.PlaybackException; | 
				
			||||
import com.google.android.exoplayer2.ext.okhttp.OkHttpDataSource; | 
				
			||||
import com.google.android.exoplayer2.upstream.DataSink; | 
				
			||||
import com.google.android.exoplayer2.upstream.DataSource; | 
				
			||||
import com.google.android.exoplayer2.upstream.DataSourceException; | 
				
			||||
import com.google.android.exoplayer2.upstream.DataSpec; | 
				
			||||
import com.google.android.exoplayer2.upstream.DummyDataSource; | 
				
			||||
import com.google.android.exoplayer2.upstream.FileDataSource; | 
				
			||||
import com.google.android.exoplayer2.upstream.PriorityDataSource; | 
				
			||||
import com.google.android.exoplayer2.upstream.TeeDataSource; | 
				
			||||
import com.google.android.exoplayer2.upstream.TransferListener; | 
				
			||||
import com.google.android.exoplayer2.upstream.cache.Cache; | 
				
			||||
import com.google.android.exoplayer2.upstream.cache.Cache.CacheException; | 
				
			||||
import com.google.android.exoplayer2.upstream.cache.CacheDataSink; | 
				
			||||
import com.google.android.exoplayer2.upstream.cache.CacheKeyFactory; | 
				
			||||
import com.google.android.exoplayer2.upstream.cache.CacheSpan; | 
				
			||||
import com.google.android.exoplayer2.upstream.cache.ContentMetadata; | 
				
			||||
import com.google.android.exoplayer2.upstream.cache.ContentMetadataMutations; | 
				
			||||
import com.google.android.exoplayer2.util.Assertions; | 
				
			||||
import com.google.android.exoplayer2.util.PriorityTaskManager; | 
				
			||||
 | 
				
			||||
import java.io.IOException; | 
				
			||||
import java.io.InterruptedIOException; | 
				
			||||
import java.lang.annotation.Documented; | 
				
			||||
import java.lang.annotation.Retention; | 
				
			||||
import java.lang.annotation.RetentionPolicy; | 
				
			||||
import java.lang.annotation.Target; | 
				
			||||
import java.util.Collections; | 
				
			||||
import java.util.List; | 
				
			||||
import java.util.Map; | 
				
			||||
 | 
				
			||||
 | 
				
			||||
/** | 
				
			||||
 * A {@link DataSource} that reads and writes a {@link Cache}. Requests are fulfilled from the cache | 
				
			||||
 * when possible. When data is not cached it is requested from an upstream {@link DataSource} and | 
				
			||||
 * written into the cache. | 
				
			||||
 */ | 
				
			||||
@SuppressWarnings("unused") | 
				
			||||
public final class OkhttpCacheDataSource implements DataSource { | 
				
			||||
 | 
				
			||||
    /** | 
				
			||||
     * {@link DataSource.Factory} for {@link OkhttpCacheDataSource} instances. | 
				
			||||
     */ | 
				
			||||
    @SuppressWarnings("unused") | 
				
			||||
    public static final class Factory implements DataSource.Factory { | 
				
			||||
 | 
				
			||||
        private Cache cache; | 
				
			||||
        private DataSource.Factory cacheReadDataSourceFactory; | 
				
			||||
        @Nullable | 
				
			||||
        private DataSink.Factory cacheWriteDataSinkFactory; | 
				
			||||
        private CacheKeyFactory cacheKeyFactory; | 
				
			||||
        private boolean cacheIsReadOnly; | 
				
			||||
        @Nullable | 
				
			||||
        private OkHttpDataSource.Factory upstreamDataSourceFactory; | 
				
			||||
        @Nullable | 
				
			||||
        private PriorityTaskManager upstreamPriorityTaskManager; | 
				
			||||
        private int upstreamPriority; | 
				
			||||
        private @OkhttpCacheDataSource.Flags int flags; | 
				
			||||
        @Nullable | 
				
			||||
        private OkhttpCacheDataSource.EventListener eventListener; | 
				
			||||
 | 
				
			||||
        public Factory() { | 
				
			||||
            cacheReadDataSourceFactory = new FileDataSource.Factory(); | 
				
			||||
            cacheKeyFactory = CacheKeyFactory.DEFAULT; | 
				
			||||
        } | 
				
			||||
 | 
				
			||||
        /** | 
				
			||||
         * Sets the cache that will be used. | 
				
			||||
         * | 
				
			||||
         * <p>Must be called before the factory is used. | 
				
			||||
         * | 
				
			||||
         * @param cache The cache that will be used. | 
				
			||||
         * @return This factory. | 
				
			||||
         */ | 
				
			||||
        public Factory setCache(Cache cache) { | 
				
			||||
            this.cache = cache; | 
				
			||||
            return this; | 
				
			||||
        } | 
				
			||||
 | 
				
			||||
        /** | 
				
			||||
         * Returns the cache that will be used, or {@code null} if {@link #setCache} has yet to be | 
				
			||||
         * called. | 
				
			||||
         */ | 
				
			||||
        @Nullable | 
				
			||||
        public Cache getCache() { | 
				
			||||
            return cache; | 
				
			||||
        } | 
				
			||||
 | 
				
			||||
        /** | 
				
			||||
         * Sets the {@link DataSource.Factory} for {@link DataSource DataSources} for reading from the | 
				
			||||
         * cache. | 
				
			||||
         * | 
				
			||||
         * <p>The default is a {@link FileDataSource.Factory} in its default configuration. | 
				
			||||
         * | 
				
			||||
         * @param cacheReadDataSourceFactory The {@link DataSource.Factory} for reading from the cache. | 
				
			||||
         * @return This factory. | 
				
			||||
         */ | 
				
			||||
        public Factory setCacheReadDataSourceFactory(DataSource.Factory cacheReadDataSourceFactory) { | 
				
			||||
            this.cacheReadDataSourceFactory = cacheReadDataSourceFactory; | 
				
			||||
            return this; | 
				
			||||
        } | 
				
			||||
 | 
				
			||||
        /** | 
				
			||||
         * Sets the {@link DataSink.Factory} for generating {@link DataSink DataSinks} for writing data | 
				
			||||
         * to the cache. Passing {@code null} causes the cache to be read-only. | 
				
			||||
         * | 
				
			||||
         * <p>The default is a {@link CacheDataSink.Factory} in its default configuration. | 
				
			||||
         * | 
				
			||||
         * @param cacheWriteDataSinkFactory The {@link DataSink.Factory} for generating {@link DataSink | 
				
			||||
         *                                  DataSinks} for writing data to the cache, or {@code null} to disable writing. | 
				
			||||
         * @return This factory. | 
				
			||||
         */ | 
				
			||||
        public Factory setCacheWriteDataSinkFactory( | 
				
			||||
                @Nullable DataSink.Factory cacheWriteDataSinkFactory) { | 
				
			||||
            this.cacheWriteDataSinkFactory = cacheWriteDataSinkFactory; | 
				
			||||
            this.cacheIsReadOnly = cacheWriteDataSinkFactory == null; | 
				
			||||
            return this; | 
				
			||||
        } | 
				
			||||
 | 
				
			||||
        /** | 
				
			||||
         * Sets the {@link CacheKeyFactory}. | 
				
			||||
         * | 
				
			||||
         * <p>The default is {@link CacheKeyFactory#DEFAULT}. | 
				
			||||
         * | 
				
			||||
         * @param cacheKeyFactory The {@link CacheKeyFactory}. | 
				
			||||
         * @return This factory. | 
				
			||||
         */ | 
				
			||||
        public Factory setCacheKeyFactory(CacheKeyFactory cacheKeyFactory) { | 
				
			||||
            this.cacheKeyFactory = cacheKeyFactory; | 
				
			||||
            return this; | 
				
			||||
        } | 
				
			||||
 | 
				
			||||
        /** | 
				
			||||
         * Returns the {@link CacheKeyFactory} that will be used. | 
				
			||||
         */ | 
				
			||||
        public CacheKeyFactory getCacheKeyFactory() { | 
				
			||||
            return cacheKeyFactory; | 
				
			||||
        } | 
				
			||||
 | 
				
			||||
        /** | 
				
			||||
         * Sets the {@link DataSource.Factory} for upstream {@link DataSource DataSources}, which are | 
				
			||||
         * used to read data in the case of a cache miss. | 
				
			||||
         * | 
				
			||||
         * <p>The default is {@code null}, and so this method must be called before the factory is used | 
				
			||||
         * in order for data to be read from upstream in the case of a cache miss. | 
				
			||||
         * | 
				
			||||
         * @param upstreamDataSourceFactory The upstream {@link DataSource} for reading data not in the | 
				
			||||
         *                                  cache, or {@code null} to cause failure in the case of a cache miss. | 
				
			||||
         * @return This factory. | 
				
			||||
         */ | 
				
			||||
        public Factory setUpstreamDataSourceFactory( | 
				
			||||
                @Nullable OkHttpDataSource.Factory upstreamDataSourceFactory) { | 
				
			||||
            this.upstreamDataSourceFactory = upstreamDataSourceFactory; | 
				
			||||
            return this; | 
				
			||||
        } | 
				
			||||
 | 
				
			||||
        public Factory setUserAgent(String userAgent) { | 
				
			||||
            if (this.upstreamDataSourceFactory != null) { | 
				
			||||
                this.upstreamDataSourceFactory.setUserAgent(userAgent); | 
				
			||||
            } | 
				
			||||
            return this; | 
				
			||||
        } | 
				
			||||
 | 
				
			||||
        public Factory setDefaultRequestProperties(Map<String, String> defaultRequestProperties) { | 
				
			||||
            if (this.upstreamDataSourceFactory != null) { | 
				
			||||
                this.upstreamDataSourceFactory.setDefaultRequestProperties(defaultRequestProperties); | 
				
			||||
            } | 
				
			||||
            return this; | 
				
			||||
        } | 
				
			||||
 | 
				
			||||
        /** | 
				
			||||
         * Sets an optional {@link PriorityTaskManager} to use when requesting data from upstream. | 
				
			||||
         * | 
				
			||||
         * <p>If set, reads from the upstream {@link DataSource} will only be allowed to proceed if | 
				
			||||
         * there are no higher priority tasks registered to the {@link PriorityTaskManager}. If there | 
				
			||||
         * exists a higher priority task then {@link PriorityTaskManager.PriorityTooLowException} will | 
				
			||||
         * be thrown instead. | 
				
			||||
         * | 
				
			||||
         * <p>Note that requests to {@link OkhttpCacheDataSource} instances are intended to be used as parts | 
				
			||||
         * of (possibly larger) tasks that are registered with the {@link PriorityTaskManager}, and | 
				
			||||
         * hence {@link OkhttpCacheDataSource} does <em>not</em> register a task by itself. This must be done | 
				
			||||
         * by the surrounding code that uses the {@link OkhttpCacheDataSource} instances. | 
				
			||||
         * | 
				
			||||
         * <p>The default is {@code null}. | 
				
			||||
         * | 
				
			||||
         * @param upstreamPriorityTaskManager The upstream {@link PriorityTaskManager}. | 
				
			||||
         * @return This factory. | 
				
			||||
         */ | 
				
			||||
        public Factory setUpstreamPriorityTaskManager( | 
				
			||||
                @Nullable PriorityTaskManager upstreamPriorityTaskManager) { | 
				
			||||
            this.upstreamPriorityTaskManager = upstreamPriorityTaskManager; | 
				
			||||
            return this; | 
				
			||||
        } | 
				
			||||
 | 
				
			||||
        /** | 
				
			||||
         * Returns the {@link PriorityTaskManager} that will bs used when requesting data from upstream, | 
				
			||||
         * or {@code null} if there is none. | 
				
			||||
         */ | 
				
			||||
        @Nullable | 
				
			||||
        public PriorityTaskManager getUpstreamPriorityTaskManager() { | 
				
			||||
            return upstreamPriorityTaskManager; | 
				
			||||
        } | 
				
			||||
 | 
				
			||||
        /** | 
				
			||||
         * Sets the priority to use when requesting data from upstream. The priority is only used if a | 
				
			||||
         * {@link PriorityTaskManager} is set by calling {@link #setUpstreamPriorityTaskManager}. | 
				
			||||
         * | 
				
			||||
         * <p>The default is {@link C#PRIORITY_PLAYBACK}. | 
				
			||||
         * | 
				
			||||
         * @param upstreamPriority The priority to use when requesting data from upstream. | 
				
			||||
         * @return This factory. | 
				
			||||
         */ | 
				
			||||
        public Factory setUpstreamPriority(int upstreamPriority) { | 
				
			||||
            this.upstreamPriority = upstreamPriority; | 
				
			||||
            return this; | 
				
			||||
        } | 
				
			||||
 | 
				
			||||
        /** | 
				
			||||
         * Sets the {@link OkhttpCacheDataSource.Flags}. | 
				
			||||
         * | 
				
			||||
         * <p>The default is {@code 0}. | 
				
			||||
         * | 
				
			||||
         * @param flags The {@link OkhttpCacheDataSource.Flags}. | 
				
			||||
         * @return This factory. | 
				
			||||
         */ | 
				
			||||
        public Factory setFlags(@OkhttpCacheDataSource.Flags int flags) { | 
				
			||||
            this.flags = flags; | 
				
			||||
            return this; | 
				
			||||
        } | 
				
			||||
 | 
				
			||||
        /** | 
				
			||||
         * Sets the {link EventListener} to which events are delivered. | 
				
			||||
         * | 
				
			||||
         * <p>The default is {@code null}. | 
				
			||||
         * | 
				
			||||
         * @param eventListener The {@link EventListener}. | 
				
			||||
         * @return This factory. | 
				
			||||
         */ | 
				
			||||
        public Factory setEventListener(@Nullable EventListener eventListener) { | 
				
			||||
            this.eventListener = eventListener; | 
				
			||||
            return this; | 
				
			||||
        } | 
				
			||||
 | 
				
			||||
        @NonNull | 
				
			||||
        @Override | 
				
			||||
        public OkhttpCacheDataSource createDataSource() { | 
				
			||||
            return createDataSourceInternal( | 
				
			||||
                    upstreamDataSourceFactory != null ? upstreamDataSourceFactory.createDataSource() : null, | 
				
			||||
                    flags, | 
				
			||||
                    upstreamPriority); | 
				
			||||
        } | 
				
			||||
 | 
				
			||||
        /** | 
				
			||||
         * Returns an instance suitable for downloading content. The created instance is equivalent to | 
				
			||||
         * one that would be created by {@link #createDataSource()}, except: | 
				
			||||
         * | 
				
			||||
         * <ul> | 
				
			||||
         *   <li>The {@link #FLAG_BLOCK_ON_CACHE} is always set. | 
				
			||||
         *   <li>The task priority is overridden to be {@link C#PRIORITY_DOWNLOAD}. | 
				
			||||
         * </ul> | 
				
			||||
         * | 
				
			||||
         * @return An instance suitable for downloading content. | 
				
			||||
         */ | 
				
			||||
        public OkhttpCacheDataSource createDataSourceForDownloading() { | 
				
			||||
            return createDataSourceInternal( | 
				
			||||
                    upstreamDataSourceFactory != null ? upstreamDataSourceFactory.createDataSource() : null, | 
				
			||||
                    flags | FLAG_BLOCK_ON_CACHE, | 
				
			||||
                    C.PRIORITY_DOWNLOAD); | 
				
			||||
        } | 
				
			||||
 | 
				
			||||
        /** | 
				
			||||
         * Returns an instance suitable for reading cached content as part of removing a download. The | 
				
			||||
         * created instance is equivalent to one that would be created by {@link #createDataSource()}, | 
				
			||||
         * except: | 
				
			||||
         * | 
				
			||||
         * <ul> | 
				
			||||
         *   <li>The upstream is overridden to be {@code null}, since when removing content we don't | 
				
			||||
         *       want to request anything that's not already cached. | 
				
			||||
         *   <li>The {@link #FLAG_BLOCK_ON_CACHE} is always set. | 
				
			||||
         *   <li>The task priority is overridden to be {@link C#PRIORITY_DOWNLOAD}. | 
				
			||||
         * </ul> | 
				
			||||
         * | 
				
			||||
         * @return An instance suitable for reading cached content as part of removing a download. | 
				
			||||
         */ | 
				
			||||
        public OkhttpCacheDataSource createDataSourceForRemovingDownload() { | 
				
			||||
            return createDataSourceInternal( | 
				
			||||
                    /* upstreamDataSource= */ null, flags | FLAG_BLOCK_ON_CACHE, C.PRIORITY_DOWNLOAD); | 
				
			||||
        } | 
				
			||||
 | 
				
			||||
        private OkhttpCacheDataSource createDataSourceInternal( | 
				
			||||
                @Nullable DataSource upstreamDataSource, @Flags int flags, int upstreamPriority) { | 
				
			||||
            Cache cache = checkNotNull(this.cache); | 
				
			||||
            @Nullable DataSink cacheWriteDataSink; | 
				
			||||
            if (cacheIsReadOnly || upstreamDataSource == null) { | 
				
			||||
                cacheWriteDataSink = null; | 
				
			||||
            } else if (cacheWriteDataSinkFactory != null) { | 
				
			||||
                cacheWriteDataSink = cacheWriteDataSinkFactory.createDataSink(); | 
				
			||||
            } else { | 
				
			||||
                cacheWriteDataSink = new CacheDataSink.Factory().setCache(cache).createDataSink(); | 
				
			||||
            } | 
				
			||||
            return new OkhttpCacheDataSource( | 
				
			||||
                    cache, | 
				
			||||
                    upstreamDataSource, | 
				
			||||
                    cacheReadDataSourceFactory.createDataSource(), | 
				
			||||
                    cacheWriteDataSink, | 
				
			||||
                    cacheKeyFactory, | 
				
			||||
                    flags, | 
				
			||||
                    upstreamPriorityTaskManager, | 
				
			||||
                    upstreamPriority, | 
				
			||||
                    eventListener); | 
				
			||||
        } | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    /** | 
				
			||||
     * Listener of {@link OkhttpCacheDataSource} events. | 
				
			||||
     */ | 
				
			||||
    public interface EventListener { | 
				
			||||
 | 
				
			||||
        /** | 
				
			||||
         * Called when bytes have been read from the cache. | 
				
			||||
         * | 
				
			||||
         * @param cacheSizeBytes  Current cache size in bytes. | 
				
			||||
         * @param cachedBytesRead Total bytes read from the cache since this method was last called. | 
				
			||||
         */ | 
				
			||||
        void onCachedBytesRead(long cacheSizeBytes, long cachedBytesRead); | 
				
			||||
 | 
				
			||||
        /** | 
				
			||||
         * Called when the current request ignores cache. | 
				
			||||
         * | 
				
			||||
         * @param reason Reason cache is bypassed. | 
				
			||||
         */ | 
				
			||||
        void onCacheIgnored(@CacheIgnoredReason int reason); | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    /** | 
				
			||||
     * Flags controlling the OkhttpCacheDataSource's behavior. Possible flag values are {@link | 
				
			||||
     * #FLAG_BLOCK_ON_CACHE}, {@link #FLAG_IGNORE_CACHE_ON_ERROR} and {@link | 
				
			||||
     * #FLAG_IGNORE_CACHE_FOR_UNSET_LENGTH_REQUESTS}. | 
				
			||||
     */ | 
				
			||||
    @Documented | 
				
			||||
    @Retention(RetentionPolicy.SOURCE) | 
				
			||||
    @Target(TYPE_USE) | 
				
			||||
    @IntDef( | 
				
			||||
            flag = true, | 
				
			||||
            value = { | 
				
			||||
                    FLAG_BLOCK_ON_CACHE, | 
				
			||||
                    FLAG_IGNORE_CACHE_ON_ERROR, | 
				
			||||
                    FLAG_IGNORE_CACHE_FOR_UNSET_LENGTH_REQUESTS | 
				
			||||
            }) | 
				
			||||
    public @interface Flags { | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    /** | 
				
			||||
     * A flag indicating whether we will block reads if the cache key is locked. If unset then data is | 
				
			||||
     * read from upstream if the cache key is locked, regardless of whether the data is cached. | 
				
			||||
     */ | 
				
			||||
    public static final int FLAG_BLOCK_ON_CACHE = 1; | 
				
			||||
 | 
				
			||||
    /** | 
				
			||||
     * A flag indicating whether the cache is bypassed following any cache related error. If set then | 
				
			||||
     * cache related exceptions may be thrown for one cycle of open, read and close calls. Subsequent | 
				
			||||
     * cycles of these calls will then bypass the cache. | 
				
			||||
     */ | 
				
			||||
    public static final int FLAG_IGNORE_CACHE_ON_ERROR = 1 << 1; // 2
 | 
				
			||||
 | 
				
			||||
    /** | 
				
			||||
     * A flag indicating that the cache should be bypassed for requests whose lengths are unset. This | 
				
			||||
     * flag is provided for legacy reasons only. | 
				
			||||
     */ | 
				
			||||
    public static final int FLAG_IGNORE_CACHE_FOR_UNSET_LENGTH_REQUESTS = 1 << 2; // 4
 | 
				
			||||
 | 
				
			||||
    /** | 
				
			||||
     * Reasons the cache may be ignored. One of {@link #CACHE_IGNORED_REASON_ERROR} or {@link | 
				
			||||
     * #CACHE_IGNORED_REASON_UNSET_LENGTH}. | 
				
			||||
     */ | 
				
			||||
    // @Target list includes both 'default' targets and TYPE_USE, to ensure backwards compatibility
 | 
				
			||||
    // with Kotlin usages from before TYPE_USE was added.
 | 
				
			||||
    @Documented | 
				
			||||
    @Retention(RetentionPolicy.SOURCE) | 
				
			||||
    @Target({FIELD, METHOD, PARAMETER, LOCAL_VARIABLE, TYPE_USE}) | 
				
			||||
    @IntDef({CACHE_IGNORED_REASON_ERROR, CACHE_IGNORED_REASON_UNSET_LENGTH}) | 
				
			||||
    public @interface CacheIgnoredReason { | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    /** | 
				
			||||
     * Cache not ignored. | 
				
			||||
     */ | 
				
			||||
    private static final int CACHE_NOT_IGNORED = -1; | 
				
			||||
 | 
				
			||||
    /** | 
				
			||||
     * Cache ignored due to a cache related error. | 
				
			||||
     */ | 
				
			||||
    public static final int CACHE_IGNORED_REASON_ERROR = 0; | 
				
			||||
 | 
				
			||||
    /** | 
				
			||||
     * Cache ignored due to a request with an unset length. | 
				
			||||
     */ | 
				
			||||
    public static final int CACHE_IGNORED_REASON_UNSET_LENGTH = 1; | 
				
			||||
 | 
				
			||||
    /** | 
				
			||||
     * Minimum number of bytes to read before checking cache for availability. | 
				
			||||
     */ | 
				
			||||
    private static final long MIN_READ_BEFORE_CHECKING_CACHE = 100 * 1024; | 
				
			||||
 | 
				
			||||
    private final Cache cache; | 
				
			||||
    private final DataSource cacheReadDataSource; | 
				
			||||
    @Nullable | 
				
			||||
    private final DataSource cacheWriteDataSource; | 
				
			||||
    private final DataSource upstreamDataSource; | 
				
			||||
    private final CacheKeyFactory cacheKeyFactory; | 
				
			||||
    @Nullable | 
				
			||||
    private final EventListener eventListener; | 
				
			||||
 | 
				
			||||
    private final boolean blockOnCache; | 
				
			||||
    private final boolean ignoreCacheOnError; | 
				
			||||
    private final boolean ignoreCacheForUnsetLengthRequests; | 
				
			||||
 | 
				
			||||
    @Nullable | 
				
			||||
    private Uri actualUri; | 
				
			||||
    @Nullable | 
				
			||||
    private DataSpec requestDataSpec; | 
				
			||||
    @Nullable | 
				
			||||
    private DataSpec currentDataSpec; | 
				
			||||
    @Nullable | 
				
			||||
    private DataSource currentDataSource; | 
				
			||||
    private long currentDataSourceBytesRead; | 
				
			||||
    private long readPosition; | 
				
			||||
    private long bytesRemaining; | 
				
			||||
    @Nullable | 
				
			||||
    private CacheSpan currentHoleSpan; | 
				
			||||
    private boolean seenCacheError; | 
				
			||||
    private boolean currentRequestIgnoresCache; | 
				
			||||
    private long totalCachedBytesRead; | 
				
			||||
    private long checkCachePosition; | 
				
			||||
 | 
				
			||||
    /** | 
				
			||||
     * Constructs an instance with default {@link DataSource} and {@link DataSink} instances for | 
				
			||||
     * reading and writing the cache. | 
				
			||||
     * | 
				
			||||
     * @param cache              The cache. | 
				
			||||
     * @param upstreamDataSource A {@link DataSource} for reading data not in the cache. If null, | 
				
			||||
     *                           reading will fail if a cache miss occurs. | 
				
			||||
     */ | 
				
			||||
    public OkhttpCacheDataSource(Cache cache, @Nullable DataSource upstreamDataSource) { | 
				
			||||
        this(cache, upstreamDataSource, /* flags= */ 0); | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    /** | 
				
			||||
     * Constructs an instance with default {@link DataSource} and {@link DataSink} instances for | 
				
			||||
     * reading and writing the cache. | 
				
			||||
     * | 
				
			||||
     * @param cache              The cache. | 
				
			||||
     * @param upstreamDataSource A {@link DataSource} for reading data not in the cache. If null, | 
				
			||||
     *                           reading will fail if a cache miss occurs. | 
				
			||||
     * @param flags              A combination of {@link #FLAG_BLOCK_ON_CACHE}, {@link #FLAG_IGNORE_CACHE_ON_ERROR} | 
				
			||||
     *                           and {@link #FLAG_IGNORE_CACHE_FOR_UNSET_LENGTH_REQUESTS}, or 0. | 
				
			||||
     */ | 
				
			||||
    public OkhttpCacheDataSource(Cache cache, @Nullable DataSource upstreamDataSource, @Flags int flags) { | 
				
			||||
        this( | 
				
			||||
                cache, | 
				
			||||
                upstreamDataSource, | 
				
			||||
                new FileDataSource(), | 
				
			||||
                new CacheDataSink(cache, CacheDataSink.DEFAULT_FRAGMENT_SIZE), | 
				
			||||
                flags, | 
				
			||||
                /* eventListener= */ null); | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    /** | 
				
			||||
     * Constructs an instance with arbitrary {@link DataSource} and {@link DataSink} instances for | 
				
			||||
     * reading and writing the cache. One use of this constructor is to allow data to be transformed | 
				
			||||
     * before it is written to disk. | 
				
			||||
     * | 
				
			||||
     * @param cache               The cache. | 
				
			||||
     * @param upstreamDataSource  A {@link DataSource} for reading data not in the cache. If null, | 
				
			||||
     *                            reading will fail if a cache miss occurs. | 
				
			||||
     * @param cacheReadDataSource A {@link DataSource} for reading data from the cache. | 
				
			||||
     * @param cacheWriteDataSink  A {@link DataSink} for writing data to the cache. If null, cache is | 
				
			||||
     *                            accessed read-only. | 
				
			||||
     * @param flags               A combination of {@link #FLAG_BLOCK_ON_CACHE}, {@link #FLAG_IGNORE_CACHE_ON_ERROR} | 
				
			||||
     *                            and {@link #FLAG_IGNORE_CACHE_FOR_UNSET_LENGTH_REQUESTS}, or 0. | 
				
			||||
     * @param eventListener       An optional {@link EventListener} to receive events. | 
				
			||||
     */ | 
				
			||||
    public OkhttpCacheDataSource( | 
				
			||||
            Cache cache, | 
				
			||||
            @Nullable DataSource upstreamDataSource, | 
				
			||||
            DataSource cacheReadDataSource, | 
				
			||||
            @Nullable DataSink cacheWriteDataSink, | 
				
			||||
            @Flags int flags, | 
				
			||||
            @Nullable EventListener eventListener) { | 
				
			||||
        this( | 
				
			||||
                cache, | 
				
			||||
                upstreamDataSource, | 
				
			||||
                cacheReadDataSource, | 
				
			||||
                cacheWriteDataSink, | 
				
			||||
                flags, | 
				
			||||
                eventListener, | 
				
			||||
                /* cacheKeyFactory= */ null); | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    /** | 
				
			||||
     * Constructs an instance with arbitrary {@link DataSource} and {@link DataSink} instances for | 
				
			||||
     * reading and writing the cache. One use of this constructor is to allow data to be transformed | 
				
			||||
     * before it is written to disk. | 
				
			||||
     * | 
				
			||||
     * @param cache               The cache. | 
				
			||||
     * @param upstreamDataSource  A {@link DataSource} for reading data not in the cache. If null, | 
				
			||||
     *                            reading will fail if a cache miss occurs. | 
				
			||||
     * @param cacheReadDataSource A {@link DataSource} for reading data from the cache. | 
				
			||||
     * @param cacheWriteDataSink  A {@link DataSink} for writing data to the cache. If null, cache is | 
				
			||||
     *                            accessed read-only. | 
				
			||||
     * @param flags               A combination of {@link #FLAG_BLOCK_ON_CACHE}, {@link #FLAG_IGNORE_CACHE_ON_ERROR} | 
				
			||||
     *                            and {@link #FLAG_IGNORE_CACHE_FOR_UNSET_LENGTH_REQUESTS}, or 0. | 
				
			||||
     * @param eventListener       An optional {@link EventListener} to receive events. | 
				
			||||
     * @param cacheKeyFactory     An optional factory for cache keys. | 
				
			||||
     */ | 
				
			||||
    public OkhttpCacheDataSource( | 
				
			||||
            Cache cache, | 
				
			||||
            @Nullable DataSource upstreamDataSource, | 
				
			||||
            DataSource cacheReadDataSource, | 
				
			||||
            @Nullable DataSink cacheWriteDataSink, | 
				
			||||
            @Flags int flags, | 
				
			||||
            @Nullable EventListener eventListener, | 
				
			||||
            @Nullable CacheKeyFactory cacheKeyFactory) { | 
				
			||||
        this( | 
				
			||||
                cache, | 
				
			||||
                upstreamDataSource, | 
				
			||||
                cacheReadDataSource, | 
				
			||||
                cacheWriteDataSink, | 
				
			||||
                cacheKeyFactory, | 
				
			||||
                flags, | 
				
			||||
                /* upstreamPriorityTaskManager= */ null, | 
				
			||||
                /* upstreamPriority= */ C.PRIORITY_PLAYBACK, | 
				
			||||
                eventListener); | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    private OkhttpCacheDataSource( | 
				
			||||
            Cache cache, | 
				
			||||
            @Nullable DataSource upstreamDataSource, | 
				
			||||
            DataSource cacheReadDataSource, | 
				
			||||
            @Nullable DataSink cacheWriteDataSink, | 
				
			||||
            @Nullable CacheKeyFactory cacheKeyFactory, | 
				
			||||
            @Flags int flags, | 
				
			||||
            @Nullable PriorityTaskManager upstreamPriorityTaskManager, | 
				
			||||
            int upstreamPriority, | 
				
			||||
            @Nullable EventListener eventListener) { | 
				
			||||
        this.cache = cache; | 
				
			||||
        this.cacheReadDataSource = cacheReadDataSource; | 
				
			||||
        this.cacheKeyFactory = cacheKeyFactory != null ? cacheKeyFactory : CacheKeyFactory.DEFAULT; | 
				
			||||
        this.blockOnCache = (flags & FLAG_BLOCK_ON_CACHE) != 0; | 
				
			||||
        this.ignoreCacheOnError = (flags & FLAG_IGNORE_CACHE_ON_ERROR) != 0; | 
				
			||||
        this.ignoreCacheForUnsetLengthRequests = | 
				
			||||
                (flags & FLAG_IGNORE_CACHE_FOR_UNSET_LENGTH_REQUESTS) != 0; | 
				
			||||
        if (upstreamDataSource != null) { | 
				
			||||
            if (upstreamPriorityTaskManager != null) { | 
				
			||||
                upstreamDataSource = | 
				
			||||
                        new PriorityDataSource( | 
				
			||||
                                upstreamDataSource, upstreamPriorityTaskManager, upstreamPriority); | 
				
			||||
            } | 
				
			||||
            this.upstreamDataSource = upstreamDataSource; | 
				
			||||
            this.cacheWriteDataSource = | 
				
			||||
                    cacheWriteDataSink != null | 
				
			||||
                            ? new TeeDataSource(upstreamDataSource, cacheWriteDataSink) | 
				
			||||
                            : null; | 
				
			||||
        } else { | 
				
			||||
            this.upstreamDataSource = DummyDataSource.INSTANCE; | 
				
			||||
            this.cacheWriteDataSource = null; | 
				
			||||
        } | 
				
			||||
        this.eventListener = eventListener; | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    /** | 
				
			||||
     * Returns the {@link Cache} used by this instance. | 
				
			||||
     */ | 
				
			||||
    public Cache getCache() { | 
				
			||||
        return cache; | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    /** | 
				
			||||
     * Returns the {@link CacheKeyFactory} used by this instance. | 
				
			||||
     */ | 
				
			||||
    public CacheKeyFactory getCacheKeyFactory() { | 
				
			||||
        return cacheKeyFactory; | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    @Override | 
				
			||||
    public void addTransferListener(@NonNull TransferListener transferListener) { | 
				
			||||
        checkNotNull(transferListener); | 
				
			||||
        cacheReadDataSource.addTransferListener(transferListener); | 
				
			||||
        upstreamDataSource.addTransferListener(transferListener); | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    @Override | 
				
			||||
    public long open(@NonNull DataSpec dataSpec) throws IOException { | 
				
			||||
        try { | 
				
			||||
            String key = cacheKeyFactory.buildCacheKey(dataSpec); | 
				
			||||
            DataSpec requestDataSpec = dataSpec.buildUpon().setKey(key).build(); | 
				
			||||
            this.requestDataSpec = requestDataSpec; | 
				
			||||
            actualUri = getRedirectedUriOrDefault(cache, key, /* defaultUri= */ requestDataSpec.uri); | 
				
			||||
            readPosition = dataSpec.position; | 
				
			||||
 | 
				
			||||
            int reason = shouldIgnoreCacheForRequest(dataSpec); | 
				
			||||
            currentRequestIgnoresCache = reason != CACHE_NOT_IGNORED; | 
				
			||||
            if (currentRequestIgnoresCache) { | 
				
			||||
                notifyCacheIgnored(reason); | 
				
			||||
            } | 
				
			||||
 | 
				
			||||
            if (currentRequestIgnoresCache) { | 
				
			||||
                bytesRemaining = C.LENGTH_UNSET; | 
				
			||||
            } else { | 
				
			||||
                bytesRemaining = ContentMetadata.getContentLength(cache.getContentMetadata(key)); | 
				
			||||
                if (bytesRemaining != C.LENGTH_UNSET) { | 
				
			||||
                    bytesRemaining -= dataSpec.position; | 
				
			||||
                    if (bytesRemaining < 0) { | 
				
			||||
                        throw new DataSourceException( | 
				
			||||
                                PlaybackException.ERROR_CODE_IO_READ_POSITION_OUT_OF_RANGE); | 
				
			||||
                    } | 
				
			||||
                } | 
				
			||||
            } | 
				
			||||
            if (dataSpec.length != C.LENGTH_UNSET) { | 
				
			||||
                bytesRemaining = | 
				
			||||
                        bytesRemaining == C.LENGTH_UNSET | 
				
			||||
                                ? dataSpec.length | 
				
			||||
                                : min(bytesRemaining, dataSpec.length); | 
				
			||||
            } | 
				
			||||
            if (bytesRemaining > 0 || bytesRemaining == C.LENGTH_UNSET) { | 
				
			||||
                openNextSource(requestDataSpec, false); | 
				
			||||
            } | 
				
			||||
            return dataSpec.length != C.LENGTH_UNSET ? dataSpec.length : bytesRemaining; | 
				
			||||
        } catch (Throwable e) { | 
				
			||||
            handleBeforeThrow(e); | 
				
			||||
            throw e; | 
				
			||||
        } | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    @Override | 
				
			||||
    public int read(byte[] buffer, int offset, int length) throws IOException { | 
				
			||||
        if (length == 0) { | 
				
			||||
            return 0; | 
				
			||||
        } | 
				
			||||
        if (bytesRemaining == 0) { | 
				
			||||
            return C.RESULT_END_OF_INPUT; | 
				
			||||
        } | 
				
			||||
        DataSpec requestDataSpec = checkNotNull(this.requestDataSpec); | 
				
			||||
        DataSpec currentDataSpec = checkNotNull(this.currentDataSpec); | 
				
			||||
        try { | 
				
			||||
            if (readPosition >= checkCachePosition) { | 
				
			||||
                openNextSource(requestDataSpec, true); | 
				
			||||
            } | 
				
			||||
            int bytesRead = checkNotNull(currentDataSource).read(buffer, offset, length); | 
				
			||||
            if (bytesRead != C.RESULT_END_OF_INPUT) { | 
				
			||||
                if (isReadingFromCache()) { | 
				
			||||
                    totalCachedBytesRead += bytesRead; | 
				
			||||
                } | 
				
			||||
                readPosition += bytesRead; | 
				
			||||
                currentDataSourceBytesRead += bytesRead; | 
				
			||||
                if (bytesRemaining != C.LENGTH_UNSET) { | 
				
			||||
                    bytesRemaining -= bytesRead; | 
				
			||||
                } | 
				
			||||
            } else if (isReadingFromUpstream() | 
				
			||||
                    && (currentDataSpec.length == C.LENGTH_UNSET | 
				
			||||
                    || currentDataSourceBytesRead < currentDataSpec.length)) { | 
				
			||||
                // We've encountered RESULT_END_OF_INPUT from the upstream DataSource at a position not
 | 
				
			||||
                // imposed by the current DataSpec. This must mean that we've reached the end of the
 | 
				
			||||
                // resource.
 | 
				
			||||
                setNoBytesRemainingAndMaybeStoreLength(castNonNull(requestDataSpec.key)); | 
				
			||||
            } else if (bytesRemaining > 0 || bytesRemaining == C.LENGTH_UNSET) { | 
				
			||||
                closeCurrentSource(); | 
				
			||||
                openNextSource(requestDataSpec, false); | 
				
			||||
                return read(buffer, offset, length); | 
				
			||||
            } | 
				
			||||
            return bytesRead; | 
				
			||||
        } catch (Throwable e) { | 
				
			||||
            handleBeforeThrow(e); | 
				
			||||
            throw e; | 
				
			||||
        } | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    @Override | 
				
			||||
    @Nullable | 
				
			||||
    public Uri getUri() { | 
				
			||||
        return actualUri; | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    @NonNull | 
				
			||||
    @Override | 
				
			||||
    public Map<String, List<String>> getResponseHeaders() { | 
				
			||||
        // TODO: Implement.
 | 
				
			||||
        return isReadingFromUpstream() | 
				
			||||
                ? upstreamDataSource.getResponseHeaders() | 
				
			||||
                : Collections.emptyMap(); | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    @Override | 
				
			||||
    public void close() throws IOException { | 
				
			||||
        requestDataSpec = null; | 
				
			||||
        actualUri = null; | 
				
			||||
        readPosition = 0; | 
				
			||||
        notifyBytesRead(); | 
				
			||||
        try { | 
				
			||||
            closeCurrentSource(); | 
				
			||||
        } catch (Throwable e) { | 
				
			||||
            handleBeforeThrow(e); | 
				
			||||
            throw e; | 
				
			||||
        } | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    /** | 
				
			||||
     * Opens the next source. If the cache contains data spanning the current read position then | 
				
			||||
     * {@link #cacheReadDataSource} is opened to read from it. Else {@link #upstreamDataSource} is | 
				
			||||
     * opened to read from the upstream source and write into the cache. | 
				
			||||
     * | 
				
			||||
     * <p>There must not be a currently open source when this method is called, except in the case | 
				
			||||
     * that {@code checkCache} is true. If {@code checkCache} is true then there must be a currently | 
				
			||||
     * open source, and it must be {@link #upstreamDataSource}. It will be closed and a new source | 
				
			||||
     * opened if it's possible to switch to reading from or writing to the cache. If a switch isn't | 
				
			||||
     * possible then the current source is left unchanged. | 
				
			||||
     * | 
				
			||||
     * @param requestDataSpec The original {@link DataSpec} to build upon for the next source. | 
				
			||||
     * @param checkCache      If true tries to switch to reading from or writing to cache instead of | 
				
			||||
     *                        reading from {@link #upstreamDataSource}, which is the currently open source. | 
				
			||||
     */ | 
				
			||||
    private void openNextSource(DataSpec requestDataSpec, boolean checkCache) throws IOException { | 
				
			||||
        @Nullable CacheSpan nextSpan; | 
				
			||||
        String key = castNonNull(requestDataSpec.key); | 
				
			||||
        if (currentRequestIgnoresCache) { | 
				
			||||
            nextSpan = null; | 
				
			||||
        } else if (blockOnCache) { | 
				
			||||
            try { | 
				
			||||
                nextSpan = cache.startReadWrite(key, readPosition, bytesRemaining); | 
				
			||||
            } catch (InterruptedException e) { | 
				
			||||
                Thread.currentThread().interrupt(); | 
				
			||||
                throw new InterruptedIOException(); | 
				
			||||
            } | 
				
			||||
        } else { | 
				
			||||
            nextSpan = cache.startReadWriteNonBlocking(key, readPosition, bytesRemaining); | 
				
			||||
        } | 
				
			||||
 | 
				
			||||
        DataSpec nextDataSpec; | 
				
			||||
        DataSource nextDataSource; | 
				
			||||
        if (nextSpan == null) { | 
				
			||||
            // The data is locked in the cache, or we're ignoring the cache. Bypass the cache and read
 | 
				
			||||
            // from upstream.
 | 
				
			||||
            nextDataSource = upstreamDataSource; | 
				
			||||
            nextDataSpec = | 
				
			||||
                    requestDataSpec.buildUpon().setPosition(readPosition).setLength(bytesRemaining).build(); | 
				
			||||
        } else if (nextSpan.isCached) { | 
				
			||||
            // Data is cached in a span file starting at nextSpan.position.
 | 
				
			||||
            Uri fileUri = Uri.fromFile(castNonNull(nextSpan.file)); | 
				
			||||
            long filePositionOffset = nextSpan.position; | 
				
			||||
            long positionInFile = readPosition - filePositionOffset; | 
				
			||||
            long length = nextSpan.length - positionInFile; | 
				
			||||
            if (bytesRemaining != C.LENGTH_UNSET) { | 
				
			||||
                length = min(length, bytesRemaining); | 
				
			||||
            } | 
				
			||||
            nextDataSpec = | 
				
			||||
                    requestDataSpec | 
				
			||||
                            .buildUpon() | 
				
			||||
                            .setUri(fileUri) | 
				
			||||
                            .setUriPositionOffset(filePositionOffset) | 
				
			||||
                            .setPosition(positionInFile) | 
				
			||||
                            .setLength(length) | 
				
			||||
                            .build(); | 
				
			||||
            nextDataSource = cacheReadDataSource; | 
				
			||||
        } else { | 
				
			||||
            // Data is not cached, and data is not locked, read from upstream with cache backing.
 | 
				
			||||
            long length; | 
				
			||||
            if (nextSpan.isOpenEnded()) { | 
				
			||||
                length = bytesRemaining; | 
				
			||||
            } else { | 
				
			||||
                length = nextSpan.length; | 
				
			||||
                if (bytesRemaining != C.LENGTH_UNSET) { | 
				
			||||
                    length = min(length, bytesRemaining); | 
				
			||||
                } | 
				
			||||
            } | 
				
			||||
            nextDataSpec = | 
				
			||||
                    requestDataSpec.buildUpon().setPosition(readPosition).setLength(length).build(); | 
				
			||||
            if (cacheWriteDataSource != null) { | 
				
			||||
                nextDataSource = cacheWriteDataSource; | 
				
			||||
            } else { | 
				
			||||
                nextDataSource = upstreamDataSource; | 
				
			||||
                cache.releaseHoleSpan(nextSpan); | 
				
			||||
                nextSpan = null; | 
				
			||||
            } | 
				
			||||
        } | 
				
			||||
 | 
				
			||||
        checkCachePosition = | 
				
			||||
                !currentRequestIgnoresCache && nextDataSource == upstreamDataSource | 
				
			||||
                        ? readPosition + MIN_READ_BEFORE_CHECKING_CACHE | 
				
			||||
                        : Long.MAX_VALUE; | 
				
			||||
        if (checkCache) { | 
				
			||||
            Assertions.checkState(isBypassingCache()); | 
				
			||||
            if (nextDataSource == upstreamDataSource) { | 
				
			||||
                // Continue reading from upstream.
 | 
				
			||||
                return; | 
				
			||||
            } | 
				
			||||
            // We're switching to reading from or writing to the cache.
 | 
				
			||||
            try { | 
				
			||||
                closeCurrentSource(); | 
				
			||||
            } catch (Throwable e) { | 
				
			||||
                if (castNonNull(nextSpan).isHoleSpan()) { | 
				
			||||
                    // Release the hole span before throwing, else we'll hold it forever.
 | 
				
			||||
                    cache.releaseHoleSpan(nextSpan); | 
				
			||||
                } | 
				
			||||
                throw e; | 
				
			||||
            } | 
				
			||||
        } | 
				
			||||
 | 
				
			||||
        if (nextSpan != null && nextSpan.isHoleSpan()) { | 
				
			||||
            currentHoleSpan = nextSpan; | 
				
			||||
        } | 
				
			||||
        currentDataSource = nextDataSource; | 
				
			||||
        currentDataSpec = nextDataSpec; | 
				
			||||
        currentDataSourceBytesRead = 0; | 
				
			||||
        long resolvedLength = nextDataSource.open(nextDataSpec); | 
				
			||||
 | 
				
			||||
        // Update bytesRemaining, actualUri and (if writing to cache) the cache metadata.
 | 
				
			||||
        ContentMetadataMutations mutations = new ContentMetadataMutations(); | 
				
			||||
        if (nextDataSpec.length == C.LENGTH_UNSET && resolvedLength != C.LENGTH_UNSET) { | 
				
			||||
            bytesRemaining = resolvedLength; | 
				
			||||
            ContentMetadataMutations.setContentLength(mutations, readPosition + bytesRemaining); | 
				
			||||
        } | 
				
			||||
        if (isReadingFromUpstream()) { | 
				
			||||
            actualUri = nextDataSource.getUri(); | 
				
			||||
            boolean isRedirected = !requestDataSpec.uri.equals(actualUri); | 
				
			||||
            ContentMetadataMutations.setRedirectedUri(mutations, isRedirected ? actualUri : null); | 
				
			||||
        } | 
				
			||||
        if (isWritingToCache()) { | 
				
			||||
            cache.applyContentMetadataMutations(key, mutations); | 
				
			||||
        } | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    private void setNoBytesRemainingAndMaybeStoreLength(String key) throws IOException { | 
				
			||||
        bytesRemaining = 0; | 
				
			||||
        if (isWritingToCache()) { | 
				
			||||
            ContentMetadataMutations mutations = new ContentMetadataMutations(); | 
				
			||||
            ContentMetadataMutations.setContentLength(mutations, readPosition); | 
				
			||||
            cache.applyContentMetadataMutations(key, mutations); | 
				
			||||
        } | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    private static Uri getRedirectedUriOrDefault(Cache cache, String key, Uri defaultUri) { | 
				
			||||
        @Nullable Uri redirectedUri = ContentMetadata.getRedirectedUri(cache.getContentMetadata(key)); | 
				
			||||
        return redirectedUri != null ? redirectedUri : defaultUri; | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    private boolean isReadingFromUpstream() { | 
				
			||||
        return !isReadingFromCache(); | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    private boolean isBypassingCache() { | 
				
			||||
        return currentDataSource == upstreamDataSource; | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    private boolean isReadingFromCache() { | 
				
			||||
        return currentDataSource == cacheReadDataSource; | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    private boolean isWritingToCache() { | 
				
			||||
        return currentDataSource == cacheWriteDataSource; | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    private void closeCurrentSource() throws IOException { | 
				
			||||
        if (currentDataSource == null) { | 
				
			||||
            return; | 
				
			||||
        } | 
				
			||||
        try { | 
				
			||||
            currentDataSource.close(); | 
				
			||||
        } finally { | 
				
			||||
            currentDataSpec = null; | 
				
			||||
            currentDataSource = null; | 
				
			||||
            if (currentHoleSpan != null) { | 
				
			||||
                cache.releaseHoleSpan(currentHoleSpan); | 
				
			||||
                currentHoleSpan = null; | 
				
			||||
            } | 
				
			||||
        } | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    private void handleBeforeThrow(Throwable exception) { | 
				
			||||
        if (isReadingFromCache() || exception instanceof CacheException) { | 
				
			||||
            seenCacheError = true; | 
				
			||||
        } | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    private int shouldIgnoreCacheForRequest(DataSpec dataSpec) { | 
				
			||||
        if (ignoreCacheOnError && seenCacheError) { | 
				
			||||
            return CACHE_IGNORED_REASON_ERROR; | 
				
			||||
        } else if (ignoreCacheForUnsetLengthRequests && dataSpec.length == C.LENGTH_UNSET) { | 
				
			||||
            return CACHE_IGNORED_REASON_UNSET_LENGTH; | 
				
			||||
        } else { | 
				
			||||
            return CACHE_NOT_IGNORED; | 
				
			||||
        } | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    private void notifyCacheIgnored(@CacheIgnoredReason int reason) { | 
				
			||||
        if (eventListener != null) { | 
				
			||||
            eventListener.onCacheIgnored(reason); | 
				
			||||
        } | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    private void notifyBytesRead() { | 
				
			||||
        if (eventListener != null && totalCachedBytesRead > 0) { | 
				
			||||
            eventListener.onCachedBytesRead(cache.getCacheSpace(), totalCachedBytesRead); | 
				
			||||
            totalCachedBytesRead = 0; | 
				
			||||
        } | 
				
			||||
    } | 
				
			||||
} | 
				
			||||
					Loading…
					
					
				
		Reference in new issue