Skip to content
Toggle navigation
P
Projects
G
Groups
S
Snippets
Help
SDK
/
exoplayer
This project
Loading...
Sign in
Toggle navigation
Go to a project
Project
Repository
Issues
0
Merge Requests
0
Pipelines
Wiki
Snippets
Settings
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Commit
fab80874
authored
Apr 21, 2020
by
olly
Committed by
Ian Baker
Apr 27, 2020
Browse files
Options
_('Browse Files')
Download
Email Patches
Plain Diff
Sanitize CacheDataSource/CacheDataSink factories
PiperOrigin-RevId: 307636959
parent
96ccb893
Hide whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
286 additions
and
80 deletions
RELEASENOTES.md
library/core/src/main/java/com/google/android/exoplayer2/offline/DownloaderConstructorHelper.java
library/core/src/main/java/com/google/android/exoplayer2/upstream/cache/CacheDataSink.java
library/core/src/main/java/com/google/android/exoplayer2/upstream/cache/CacheDataSinkFactory.java
library/core/src/main/java/com/google/android/exoplayer2/upstream/cache/CacheDataSource.java
library/core/src/main/java/com/google/android/exoplayer2/upstream/cache/CacheDataSourceFactory.java
RELEASENOTES.md
View file @
fab80874
...
...
@@ -73,6 +73,8 @@
*
Fix
`AdsMediaSource`
child
`MediaSource`
s not being released.
*
Parse track titles from Matroska files
(
[
#7247
](
https://github.com/google/ExoPlayer/pull/7247
)
).
*
Replace
`CacheDataSinkFactory`
and
`CacheDataSourceFactory`
with
`CacheDataSink.Factory`
and
`CacheDataSource.Factory`
respectively.
*
Text:
*
Parse
`<ruby>`
and
`<rt>`
tags in WebVTT subtitles (rendering is coming
later).
...
...
library/core/src/main/java/com/google/android/exoplayer2/offline/DownloaderConstructorHelper.java
View file @
fab80874
...
...
@@ -19,14 +19,11 @@ import androidx.annotation.Nullable;
import
com.google.android.exoplayer2.C
;
import
com.google.android.exoplayer2.upstream.DataSink
;
import
com.google.android.exoplayer2.upstream.DataSource
;
import
com.google.android.exoplayer2.upstream.DummyDataSource
;
import
com.google.android.exoplayer2.upstream.FileDataSource
;
import
com.google.android.exoplayer2.upstream.PriorityDataSourceFactory
;
import
com.google.android.exoplayer2.upstream.cache.Cache
;
import
com.google.android.exoplayer2.upstream.cache.CacheDataSink
;
import
com.google.android.exoplayer2.upstream.cache.CacheDataSinkFactory
;
import
com.google.android.exoplayer2.upstream.cache.CacheDataSource
;
import
com.google.android.exoplayer2.upstream.cache.CacheDataSourceFactory
;
import
com.google.android.exoplayer2.upstream.cache.CacheKeyFactory
;
import
com.google.android.exoplayer2.util.PriorityTaskManager
;
...
...
@@ -34,8 +31,8 @@ import com.google.android.exoplayer2.util.PriorityTaskManager;
public
final
class
DownloaderConstructorHelper
{
@Nullable
private
final
PriorityTaskManager
priorityTaskManager
;
private
final
CacheDataSourceFactory
onlineCacheDataSourceFactory
;
private
final
CacheDataSourceFactory
offlineCacheDataSourceFactory
;
private
final
CacheDataSource
.
Factory
onlineCacheDataSourceFactory
;
private
final
CacheDataSource
.
Factory
offlineCacheDataSourceFactory
;
/**
* @param cache Cache instance to be used to store downloaded data.
...
...
@@ -100,37 +97,32 @@ public final class DownloaderConstructorHelper {
@Nullable
DataSink
.
Factory
cacheWriteDataSinkFactory
,
@Nullable
PriorityTaskManager
priorityTaskManager
,
@Nullable
CacheKeyFactory
cacheKeyFactory
)
{
this
.
priorityTaskManager
=
priorityTaskManager
;
if
(
priorityTaskManager
!=
null
)
{
upstreamFactory
=
new
PriorityDataSourceFactory
(
upstreamFactory
,
priorityTaskManager
,
C
.
PRIORITY_DOWNLOAD
);
}
DataSource
.
Factory
readDataSourceFactory
=
cacheReadDataSourceFactory
!=
null
?
cacheReadDataSourceFactory
:
new
FileDataSource
.
Factory
();
if
(
cacheWriteDataSinkFactory
==
null
)
{
cacheWriteDataSinkFactory
=
new
CacheDataSinkFactory
(
cache
,
CacheDataSink
.
DEFAULT_FRAGMENT_SIZE
);
}
onlineCacheDataSourceFactory
=
new
CacheDataSourceFactory
(
cache
,
upstreamFactory
,
readDataSourceFactory
,
cacheWriteDataSinkFactory
,
CacheDataSource
.
FLAG_BLOCK_ON_CACHE
,
/* eventListener= */
null
,
cacheKeyFactory
);
new
CacheDataSource
.
Factory
()
.
setCache
(
cache
)
.
setUpstreamDataSourceFactory
(
upstreamFactory
)
.
setFlags
(
CacheDataSource
.
FLAG_BLOCK_ON_CACHE
);
offlineCacheDataSourceFactory
=
new
CacheDataSourceFactory
(
cache
,
DummyDataSource
.
FACTORY
,
readDataSourceFactory
,
null
,
CacheDataSource
.
FLAG_BLOCK_ON_CACHE
,
/* eventListener= */
null
,
cacheKeyFactory
);
this
.
priorityTaskManager
=
priorityTaskManager
;
new
CacheDataSource
.
Factory
()
.
setCache
(
cache
)
.
setCacheWriteDataSinkFactory
(
null
)
.
setFlags
(
CacheDataSource
.
FLAG_BLOCK_ON_CACHE
);
if
(
cacheKeyFactory
!=
null
)
{
onlineCacheDataSourceFactory
.
setCacheKeyFactory
(
cacheKeyFactory
);
offlineCacheDataSourceFactory
.
setCacheKeyFactory
(
cacheKeyFactory
);
}
if
(
cacheReadDataSourceFactory
!=
null
)
{
onlineCacheDataSourceFactory
.
setCacheReadDataSourceFactory
(
cacheReadDataSourceFactory
);
offlineCacheDataSourceFactory
.
setCacheReadDataSourceFactory
(
cacheReadDataSourceFactory
);
}
if
(
cacheWriteDataSinkFactory
!=
null
)
{
onlineCacheDataSourceFactory
.
setCacheWriteDataSinkFactory
(
cacheWriteDataSinkFactory
);
}
}
/** Returns a {@link PriorityTaskManager} instance. */
...
...
library/core/src/main/java/com/google/android/exoplayer2/upstream/cache/CacheDataSink.java
View file @
fab80874
...
...
@@ -39,6 +39,78 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
*/
public
final
class
CacheDataSink
implements
DataSink
{
/** {@link DataSink.Factory} for {@link CacheDataSink} instances. */
public
static
final
class
Factory
implements
DataSink
.
Factory
{
private
@MonotonicNonNull
Cache
cache
;
private
long
fragmentSize
;
private
int
bufferSize
;
/** Creates an instance. */
public
Factory
()
{
fragmentSize
=
CacheDataSink
.
DEFAULT_FRAGMENT_SIZE
;
bufferSize
=
CacheDataSink
.
DEFAULT_BUFFER_SIZE
;
}
/**
* Sets the cache to which data will be written.
*
* <p>Must be called before the factory is used.
*
* @param cache The cache to which data will be written.
* @return This factory.
*/
public
Factory
setCache
(
Cache
cache
)
{
this
.
cache
=
cache
;
return
this
;
}
/**
* Sets the cache file fragment size. For requests that should be fragmented into multiple cache
* files, this is the maximum size of a cache file in bytes. If set to {@link C#LENGTH_UNSET}
* then no fragmentation will occur. Using a small value allows for finer-grained cache eviction
* policies, at the cost of increased overhead both on the cache implementation and the file
* system. Values under {@code (2 * 1024 * 1024)} are not recommended.
*
* <p>The default value is {@link CacheDataSink#DEFAULT_FRAGMENT_SIZE}.
*
* @param fragmentSize The fragment size in bytes, or {@link C#LENGTH_UNSET} to disable
* fragmentation.
* @return This factory.
*/
public
Factory
setFragmentSize
(
long
fragmentSize
)
{
this
.
fragmentSize
=
fragmentSize
;
return
this
;
}
/**
* Sets the size of an in-memory buffer used when writing to a cache file. A zero or negative
* value disables buffering.
*
* <p>The default value is {@link CacheDataSink#DEFAULT_BUFFER_SIZE}.
*
* @param bufferSize The buffer size in bytes.
* @return This factory.
*/
public
Factory
setBufferSize
(
int
bufferSize
)
{
this
.
bufferSize
=
bufferSize
;
return
this
;
}
@Override
public
DataSink
createDataSink
()
{
return
new
CacheDataSink
(
Assertions
.
checkNotNull
(
cache
),
fragmentSize
,
bufferSize
);
}
}
/** Thrown when an {@link IOException} is encountered when writing data to the sink. */
public
static
final
class
CacheDataSinkException
extends
CacheException
{
public
CacheDataSinkException
(
IOException
cause
)
{
super
(
cause
);
}
}
/** Default {@code fragmentSize} recommended for caching use cases. */
public
static
final
long
DEFAULT_FRAGMENT_SIZE
=
5
*
1024
*
1024
;
/** Default buffer size in bytes. */
...
...
@@ -60,17 +132,6 @@ public final class CacheDataSink implements DataSink {
private
@MonotonicNonNull
ReusableBufferedOutputStream
bufferedOutputStream
;
/**
* Thrown when IOException is encountered when writing data into sink.
*/
public
static
class
CacheDataSinkException
extends
CacheException
{
public
CacheDataSinkException
(
IOException
cause
)
{
super
(
cause
);
}
}
/**
* Constructs an instance using {@link #DEFAULT_BUFFER_SIZE}.
*
* @param cache The cache into which data should be written.
...
...
library/core/src/main/java/com/google/android/exoplayer2/upstream/cache/CacheDataSinkFactory.java
View file @
fab80874
...
...
@@ -17,9 +17,8 @@ package com.google.android.exoplayer2.upstream.cache;
import
com.google.android.exoplayer2.upstream.DataSink
;
/**
* A {@link DataSink.Factory} that produces {@link CacheDataSink}.
*/
/** @deprecated Use {@link CacheDataSink.Factory}. */
@Deprecated
public
final
class
CacheDataSinkFactory
implements
DataSink
.
Factory
{
private
final
Cache
cache
;
...
...
library/core/src/main/java/com/google/android/exoplayer2/upstream/cache/CacheDataSource.java
View file @
fab80874
...
...
@@ -23,6 +23,7 @@ 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.TeeDataSource
;
import
com.google.android.exoplayer2.upstream.TransferListener
;
...
...
@@ -36,6 +37,7 @@ import java.lang.annotation.RetentionPolicy;
import
java.util.Collections
;
import
java.util.List
;
import
java.util.Map
;
import
org.checkerframework.checker.nullness.qual.MonotonicNonNull
;
/**
* A {@link DataSource} that reads and writes a {@link Cache}. Requests are fulfilled from the cache
...
...
@@ -44,6 +46,167 @@ import java.util.Map;
*/
public
final
class
CacheDataSource
implements
DataSource
{
/** {@link DataSource.Factory} for {@link CacheDataSource} instances. */
public
static
final
class
Factory
implements
DataSource
.
Factory
{
private
@MonotonicNonNull
Cache
cache
;
private
DataSource
.
Factory
cacheReadDataSourceFactory
;
@Nullable
private
DataSink
.
Factory
cacheWriteDataSinkFactory
;
private
CacheKeyFactory
cacheKeyFactory
;
private
boolean
cacheIsReadOnly
;
@Nullable
private
DataSource
.
Factory
upstreamDataSourceFactory
;
@CacheDataSource
.
Flags
private
int
flags
;
@Nullable
private
CacheDataSource
.
EventListener
eventListener
;
public
Factory
()
{
cacheReadDataSourceFactory
=
new
FileDataSource
.
Factory
();
cacheKeyFactory
=
CacheUtil
.
DEFAULT_CACHE_KEY_FACTORY
;
}
/**
* 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
;
}
/**
* 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 CacheUtil#DEFAULT_CACHE_KEY_FACTORY}.
*
* @param cacheKeyFactory The {@link CacheKeyFactory}.
* @return This factory.
*/
public
Factory
setCacheKeyFactory
(
CacheKeyFactory
cacheKeyFactory
)
{
this
.
cacheKeyFactory
=
cacheKeyFactory
;
return
this
;
}
/**
* 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
DataSource
.
Factory
upstreamDataSourceFactory
)
{
this
.
upstreamDataSourceFactory
=
upstreamDataSourceFactory
;
return
this
;
}
/**
* Sets the {@link CacheDataSource.Flags}.
*
* <p>The default is {@code 0}.
*
* @param flags The {@link CacheDataSource.Flags}.
* @return This factory.
*/
public
Factory
setFlags
(
@CacheDataSource
.
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
;
}
@Override
public
CacheDataSource
createDataSource
()
{
Cache
cache
=
Assertions
.
checkNotNull
(
this
.
cache
);
@Nullable
DataSource
upstreamDataSource
=
upstreamDataSourceFactory
!=
null
?
upstreamDataSourceFactory
.
createDataSource
()
:
null
;
@Nullable
DataSink
cacheWriteDataSink
;
if
(
cacheIsReadOnly
)
{
cacheWriteDataSink
=
null
;
}
else
if
(
cacheWriteDataSinkFactory
!=
null
)
{
cacheWriteDataSink
=
cacheWriteDataSinkFactory
.
createDataSink
();
}
else
{
cacheWriteDataSink
=
new
CacheDataSink
.
Factory
().
setCache
(
cache
).
createDataSink
();
}
return
new
CacheDataSource
(
cache
,
upstreamDataSource
,
cacheReadDataSourceFactory
.
createDataSource
(),
cacheWriteDataSink
,
flags
,
eventListener
,
cacheKeyFactory
);
}
}
/** Listener of {@link CacheDataSource} 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 CacheDataSource's behavior. Possible flag values are {@link
* #FLAG_BLOCK_ON_CACHE}, {@link #FLAG_IGNORE_CACHE_ON_ERROR} and {@link
...
...
@@ -96,27 +259,6 @@ public final class CacheDataSource implements DataSource {
/** Cache ignored due to a request with an unset length. */
public
static
final
int
CACHE_IGNORED_REASON_UNSET_LENGTH
=
1
;
/**
* Listener of {@link CacheDataSource} 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
);
}
/** Minimum number of bytes to read before checking cache for availability. */
private
static
final
long
MIN_READ_BEFORE_CHECKING_CACHE
=
100
*
1024
;
...
...
@@ -148,10 +290,11 @@ public final class CacheDataSource implements DataSource {
* reading and writing the cache.
*
* @param cache The cache.
* @param upstream A {@link DataSource} for reading data not in 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
CacheDataSource
(
Cache
cache
,
DataSource
upstream
)
{
this
(
cache
,
upstream
,
/* flags= */
0
);
public
CacheDataSource
(
Cache
cache
,
@Nullable
DataSource
upstreamDataSource
)
{
this
(
cache
,
upstream
DataSource
,
/* flags= */
0
);
}
/**
...
...
@@ -159,14 +302,15 @@ public final class CacheDataSource implements DataSource {
* reading and writing the cache.
*
* @param cache The cache.
* @param upstream A {@link DataSource} for reading data not in 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
CacheDataSource
(
Cache
cache
,
DataSource
upstream
,
@Flags
int
flags
)
{
public
CacheDataSource
(
Cache
cache
,
@Nullable
DataSource
upstreamDataSource
,
@Flags
int
flags
)
{
this
(
cache
,
upstream
,
upstream
DataSource
,
new
FileDataSource
(),
new
CacheDataSink
(
cache
,
CacheDataSink
.
DEFAULT_FRAGMENT_SIZE
),
flags
,
...
...
@@ -179,7 +323,8 @@ public final class CacheDataSource implements DataSource {
* before it is written to disk.
*
* @param cache The cache.
* @param upstream A {@link DataSource} for reading data not in 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.
...
...
@@ -189,14 +334,14 @@ public final class CacheDataSource implements DataSource {
*/
public
CacheDataSource
(
Cache
cache
,
DataSource
upstream
,
@Nullable
DataSource
upstreamDataSource
,
DataSource
cacheReadDataSource
,
@Nullable
DataSink
cacheWriteDataSink
,
@Flags
int
flags
,
@Nullable
EventListener
eventListener
)
{
this
(
cache
,
upstream
,
upstream
DataSource
,
cacheReadDataSource
,
cacheWriteDataSink
,
flags
,
...
...
@@ -210,7 +355,8 @@ public final class CacheDataSource implements DataSource {
* before it is written to disk.
*
* @param cache The cache.
* @param upstream A {@link DataSource} for reading data not in 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.
...
...
@@ -221,7 +367,7 @@ public final class CacheDataSource implements DataSource {
*/
public
CacheDataSource
(
Cache
cache
,
DataSource
upstream
,
@Nullable
DataSource
upstreamDataSource
,
DataSource
cacheReadDataSource
,
@Nullable
DataSink
cacheWriteDataSink
,
@Flags
int
flags
,
...
...
@@ -235,10 +381,14 @@ public final class CacheDataSource implements DataSource {
this
.
ignoreCacheOnError
=
(
flags
&
FLAG_IGNORE_CACHE_ON_ERROR
)
!=
0
;
this
.
ignoreCacheForUnsetLengthRequests
=
(
flags
&
FLAG_IGNORE_CACHE_FOR_UNSET_LENGTH_REQUESTS
)
!=
0
;
this
.
upstreamDataSource
=
upstream
;
if
(
cacheWriteDataSink
!=
null
)
{
this
.
cacheWriteDataSource
=
new
TeeDataSource
(
upstream
,
cacheWriteDataSink
);
if
(
upstreamDataSource
!=
null
)
{
this
.
upstreamDataSource
=
upstreamDataSource
;
this
.
cacheWriteDataSource
=
cacheWriteDataSink
!=
null
?
new
TeeDataSource
(
upstreamDataSource
,
cacheWriteDataSink
)
:
null
;
}
else
{
this
.
upstreamDataSource
=
DummyDataSource
.
INSTANCE
;
this
.
cacheWriteDataSource
=
null
;
}
this
.
eventListener
=
eventListener
;
...
...
library/core/src/main/java/com/google/android/exoplayer2/upstream/cache/CacheDataSourceFactory.java
View file @
fab80874
...
...
@@ -20,7 +20,8 @@ import com.google.android.exoplayer2.upstream.DataSink;
import
com.google.android.exoplayer2.upstream.DataSource
;
import
com.google.android.exoplayer2.upstream.FileDataSource
;
/** A {@link DataSource.Factory} that produces {@link CacheDataSource}. */
/** @deprecated Use {@link CacheDataSource.Factory}. */
@Deprecated
public
final
class
CacheDataSourceFactory
implements
DataSource
.
Factory
{
private
final
Cache
cache
;
...
...
@@ -44,6 +45,7 @@ public final class CacheDataSourceFactory implements DataSource.Factory {
}
/** @see CacheDataSource#CacheDataSource(Cache, DataSource, int) */
@SuppressWarnings
(
"deprecation"
)
public
CacheDataSourceFactory
(
Cache
cache
,
DataSource
.
Factory
upstreamFactory
,
@CacheDataSource
.
Flags
int
flags
)
{
this
(
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment