Make CronetDataSourceFactory an inner class of CronetDataSource

#exofixit

PiperOrigin-RevId: 348444280
This commit is contained in:
bachinger 2020-12-21 11:55:33 +00:00 committed by Oliver Woodman
parent df0b74e4c4
commit 62720858ed
6 changed files with 349 additions and 169 deletions

View File

@ -73,6 +73,8 @@
([#1807](https://github.com/google/ExoPlayer/issues/1807)). ([#1807](https://github.com/google/ExoPlayer/issues/1807)).
* OkHttp extension: * OkHttp extension:
* Add `OkHttpDataSource.Factory` and deprecate `OkHttpDataSourceFactory`. * Add `OkHttpDataSource.Factory` and deprecate `OkHttpDataSourceFactory`.
* Cronet extension:
* Add `CronetDataSource.Factory` and deprecate `CronetDataSourceFactory`.
* Media2 extension * Media2 extension
* Make media2-extension depend on AndroidX media2:media2-session:1.1.0 to * Make media2-extension depend on AndroidX media2:media2-session:1.1.0 to
fix a deadlock while creating PlaybackStateCompat internally. fix a deadlock while creating PlaybackStateCompat internally.

View File

@ -20,7 +20,7 @@ import com.google.android.exoplayer2.DefaultRenderersFactory;
import com.google.android.exoplayer2.RenderersFactory; import com.google.android.exoplayer2.RenderersFactory;
import com.google.android.exoplayer2.database.DatabaseProvider; import com.google.android.exoplayer2.database.DatabaseProvider;
import com.google.android.exoplayer2.database.ExoDatabaseProvider; import com.google.android.exoplayer2.database.ExoDatabaseProvider;
import com.google.android.exoplayer2.ext.cronet.CronetDataSourceFactory; import com.google.android.exoplayer2.ext.cronet.CronetDataSource;
import com.google.android.exoplayer2.ext.cronet.CronetEngineWrapper; import com.google.android.exoplayer2.ext.cronet.CronetEngineWrapper;
import com.google.android.exoplayer2.offline.ActionFileUpgradeUtil; import com.google.android.exoplayer2.offline.ActionFileUpgradeUtil;
import com.google.android.exoplayer2.offline.DefaultDownloadIndex; import com.google.android.exoplayer2.offline.DefaultDownloadIndex;
@ -81,7 +81,7 @@ public final class DemoUtil {
context = context.getApplicationContext(); context = context.getApplicationContext();
CronetEngineWrapper cronetEngineWrapper = new CronetEngineWrapper(context); CronetEngineWrapper cronetEngineWrapper = new CronetEngineWrapper(context);
httpDataSourceFactory = httpDataSourceFactory =
new CronetDataSourceFactory(cronetEngineWrapper, Executors.newSingleThreadExecutor()); new CronetDataSource.Factory(cronetEngineWrapper, Executors.newSingleThreadExecutor());
} }
return httpDataSourceFactory; return httpDataSourceFactory;
} }

View File

@ -21,6 +21,7 @@ dependencies {
compileOnly 'org.jetbrains.kotlin:kotlin-annotations-jvm:' + kotlinAnnotationsVersion compileOnly 'org.jetbrains.kotlin:kotlin-annotations-jvm:' + kotlinAnnotationsVersion
testImplementation project(modulePrefix + 'library') testImplementation project(modulePrefix + 'library')
testImplementation project(modulePrefix + 'testutils') testImplementation project(modulePrefix + 'testutils')
testImplementation 'com.squareup.okhttp3:mockwebserver:' + mockWebServerVersion
testImplementation 'org.robolectric:robolectric:' + robolectricVersion testImplementation 'org.robolectric:robolectric:' + robolectricVersion
} }

View File

@ -25,9 +25,12 @@ import androidx.annotation.Nullable;
import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.ExoPlayerLibraryInfo; import com.google.android.exoplayer2.ExoPlayerLibraryInfo;
import com.google.android.exoplayer2.upstream.BaseDataSource; import com.google.android.exoplayer2.upstream.BaseDataSource;
import com.google.android.exoplayer2.upstream.DataSource;
import com.google.android.exoplayer2.upstream.DataSourceException; import com.google.android.exoplayer2.upstream.DataSourceException;
import com.google.android.exoplayer2.upstream.DataSpec; import com.google.android.exoplayer2.upstream.DataSpec;
import com.google.android.exoplayer2.upstream.DefaultHttpDataSource;
import com.google.android.exoplayer2.upstream.HttpDataSource; import com.google.android.exoplayer2.upstream.HttpDataSource;
import com.google.android.exoplayer2.upstream.TransferListener;
import com.google.android.exoplayer2.util.Assertions; import com.google.android.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.Clock; import com.google.android.exoplayer2.util.Clock;
import com.google.android.exoplayer2.util.ConditionVariable; import com.google.android.exoplayer2.util.ConditionVariable;
@ -65,6 +68,186 @@ import org.chromium.net.UrlResponseInfo;
*/ */
public class CronetDataSource extends BaseDataSource implements HttpDataSource { public class CronetDataSource extends BaseDataSource implements HttpDataSource {
static {
ExoPlayerLibraryInfo.registerModule("goog.exo.cronet");
}
/** {@link DataSource.Factory} for {@link CronetDataSource} instances. */
public static final class Factory implements HttpDataSource.Factory {
private final CronetEngineWrapper cronetEngineWrapper;
private final Executor executor;
private final RequestProperties defaultRequestProperties;
private final DefaultHttpDataSource.Factory internalFallbackFactory;
@Nullable private HttpDataSource.Factory fallbackFactory;
@Nullable private Predicate<String> contentTypePredicate;
@Nullable private TransferListener transferListener;
private int connectTimeoutMs;
private int readTimeoutMs;
private boolean resetTimeoutOnRedirects;
private boolean handleSetCookieRequests;
/**
* Creates an instance.
*
* @param cronetEngineWrapper A {@link CronetEngineWrapper}.
* @param executor The {@link java.util.concurrent.Executor} that will handle responses. This
* may be a direct executor (i.e. executes tasks on the calling thread) in order to avoid a
* thread hop from Cronet's internal network thread to the response handling thread.
* However, to avoid slowing down overall network performance, care must be taken to make
* sure response handling is a fast operation when using a direct executor.
*/
public Factory(CronetEngineWrapper cronetEngineWrapper, Executor executor) {
this.cronetEngineWrapper = cronetEngineWrapper;
this.executor = executor;
defaultRequestProperties = new RequestProperties();
internalFallbackFactory = new DefaultHttpDataSource.Factory();
connectTimeoutMs = DEFAULT_CONNECT_TIMEOUT_MILLIS;
readTimeoutMs = DEFAULT_READ_TIMEOUT_MILLIS;
}
/** @deprecated Use {@link #setDefaultRequestProperties(Map)} instead. */
@Deprecated
@Override
public final RequestProperties getDefaultRequestProperties() {
return defaultRequestProperties;
}
@Override
public final Factory setDefaultRequestProperties(Map<String, String> defaultRequestProperties) {
this.defaultRequestProperties.clearAndSet(defaultRequestProperties);
internalFallbackFactory.setDefaultRequestProperties(defaultRequestProperties);
return this;
}
/**
* Sets the connect timeout, in milliseconds.
*
* <p>The default is {@link CronetDataSource#DEFAULT_CONNECT_TIMEOUT_MILLIS}.
*
* @param connectTimeoutMs The connect timeout, in milliseconds, that will be used.
* @return This factory.
*/
public Factory setConnectionTimeoutMs(int connectTimeoutMs) {
this.connectTimeoutMs = connectTimeoutMs;
internalFallbackFactory.setConnectTimeoutMs(connectTimeoutMs);
return this;
}
/**
* Sets whether the connect timeout is reset when a redirect occurs.
*
* <p>The default is {@code false}.
*
* @param resetTimeoutOnRedirects Whether the connect timeout is reset when a redirect occurs.
* @return This factory.
*/
public Factory setResetTimeoutOnRedirects(boolean resetTimeoutOnRedirects) {
this.resetTimeoutOnRedirects = resetTimeoutOnRedirects;
return this;
}
/**
* Sets whether "Set-Cookie" requests on redirect should be forwarded to the redirect url in the
* "Cookie" header.
*
* <p>The default is {@code false}.
*
* @param handleSetCookieRequests Whether "Set-Cookie" requests on redirect should be forwarded
* to the redirect url in the "Cookie" header.
* @return This factory.
*/
public Factory setHandleSetCookieRequests(boolean handleSetCookieRequests) {
this.handleSetCookieRequests = handleSetCookieRequests;
return this;
}
/**
* Sets the read timeout, in milliseconds.
*
* <p>The default is {@link CronetDataSource#DEFAULT_READ_TIMEOUT_MILLIS}.
*
* @param readTimeoutMs The connect timeout, in milliseconds, that will be used.
* @return This factory.
*/
public Factory setReadTimeoutMs(int readTimeoutMs) {
this.readTimeoutMs = readTimeoutMs;
internalFallbackFactory.setReadTimeoutMs(readTimeoutMs);
return this;
}
/**
* Sets a content type {@link Predicate}. If a content type is rejected by the predicate then a
* {@link HttpDataSource.InvalidContentTypeException} is thrown from {@link #open(DataSpec)}.
*
* <p>The default is {@code null}.
*
* @param contentTypePredicate The content type {@link Predicate}, or {@code null} to clear a
* predicate that was previously set.
* @return This factory.
*/
public Factory setContentTypePredicate(@Nullable Predicate<String> contentTypePredicate) {
this.contentTypePredicate = contentTypePredicate;
internalFallbackFactory.setContentTypePredicate(contentTypePredicate);
return this;
}
/**
* Sets the {@link TransferListener} that will be used.
*
* <p>The default is {@code null}.
*
* <p>See {@link DataSource#addTransferListener(TransferListener)}.
*
* @param transferListener The listener that will be used.
* @return This factory.
*/
public Factory setTransferListener(@Nullable TransferListener transferListener) {
this.transferListener = transferListener;
internalFallbackFactory.setTransferListener(transferListener);
return this;
}
/**
* Sets the fallback {@link HttpDataSource.Factory} that is used as a fallback if the {@link
* CronetEngineWrapper} fails to provide a {@link CronetEngine}.
*
* <p>By default a {@link DefaultHttpDataSource} is used as fallback factory.
*
* @param fallbackFactory The fallback factory that will be used.
* @return This factory.
*/
public Factory setFallbackFactory(@Nullable HttpDataSource.Factory fallbackFactory) {
this.fallbackFactory = fallbackFactory;
return this;
}
@Override
public HttpDataSource createDataSource() {
@Nullable CronetEngine cronetEngine = cronetEngineWrapper.getCronetEngine();
if (cronetEngine == null) {
return (fallbackFactory != null)
? fallbackFactory.createDataSource()
: internalFallbackFactory.createDataSource();
}
CronetDataSource dataSource =
new CronetDataSource(
cronetEngine,
executor,
connectTimeoutMs,
readTimeoutMs,
resetTimeoutOnRedirects,
handleSetCookieRequests,
defaultRequestProperties,
contentTypePredicate);
if (transferListener != null) {
dataSource.addTransferListener(transferListener);
}
return dataSource;
}
}
/** /**
* Thrown when an error is encountered when trying to open a {@link CronetDataSource}. * Thrown when an error is encountered when trying to open a {@link CronetDataSource}.
*/ */
@ -85,20 +268,11 @@ public class CronetDataSource extends BaseDataSource implements HttpDataSource {
super(errorMessage, dataSpec, TYPE_OPEN); super(errorMessage, dataSpec, TYPE_OPEN);
this.cronetConnectionStatus = cronetConnectionStatus; this.cronetConnectionStatus = cronetConnectionStatus;
} }
} }
static { /** The default connection timeout, in milliseconds. */
ExoPlayerLibraryInfo.registerModule("goog.exo.cronet");
}
/**
* The default connection timeout, in milliseconds.
*/
public static final int DEFAULT_CONNECT_TIMEOUT_MILLIS = 8 * 1000; public static final int DEFAULT_CONNECT_TIMEOUT_MILLIS = 8 * 1000;
/** /** The default read timeout, in milliseconds. */
* The default read timeout, in milliseconds.
*/
public static final int DEFAULT_READ_TIMEOUT_MILLIS = 8 * 1000; public static final int DEFAULT_READ_TIMEOUT_MILLIS = 8 * 1000;
/* package */ final UrlRequest.Callback urlRequestCallback; /* package */ final UrlRequest.Callback urlRequestCallback;
@ -149,16 +323,9 @@ public class CronetDataSource extends BaseDataSource implements HttpDataSource {
private volatile long currentConnectTimeoutMs; private volatile long currentConnectTimeoutMs;
/** /** @deprecated Use {@link CronetDataSource.Factory} instead. */
* Creates an instance. @SuppressWarnings("deprecation")
* @Deprecated
* @param cronetEngine A CronetEngine.
* @param executor The {@link java.util.concurrent.Executor} that will handle responses. This may
* be a direct executor (i.e. executes tasks on the calling thread) in order to avoid a thread
* hop from Cronet's internal network thread to the response handling thread. However, to
* avoid slowing down overall network performance, care must be taken to make sure response
* handling is a fast operation when using a direct executor.
*/
public CronetDataSource(CronetEngine cronetEngine, Executor executor) { public CronetDataSource(CronetEngine cronetEngine, Executor executor) {
this( this(
cronetEngine, cronetEngine,
@ -169,21 +336,8 @@ public class CronetDataSource extends BaseDataSource implements HttpDataSource {
/* defaultRequestProperties= */ null); /* defaultRequestProperties= */ null);
} }
/** /** @deprecated Use {@link CronetDataSource.Factory} instead. */
* Creates an instance. @Deprecated
*
* @param cronetEngine A CronetEngine.
* @param executor The {@link java.util.concurrent.Executor} that will handle responses. This may
* be a direct executor (i.e. executes tasks on the calling thread) in order to avoid a thread
* hop from Cronet's internal network thread to the response handling thread. However, to
* avoid slowing down overall network performance, care must be taken to make sure response
* handling is a fast operation when using a direct executor.
* @param connectTimeoutMs The connection timeout, in milliseconds.
* @param readTimeoutMs The read timeout, in milliseconds.
* @param resetTimeoutOnRedirects Whether the connect timeout is reset when a redirect occurs.
* @param defaultRequestProperties Optional default {@link RequestProperties} to be sent to the
* server as HTTP headers on every request.
*/
public CronetDataSource( public CronetDataSource(
CronetEngine cronetEngine, CronetEngine cronetEngine,
Executor executor, Executor executor,
@ -197,28 +351,13 @@ public class CronetDataSource extends BaseDataSource implements HttpDataSource {
connectTimeoutMs, connectTimeoutMs,
readTimeoutMs, readTimeoutMs,
resetTimeoutOnRedirects, resetTimeoutOnRedirects,
Clock.DEFAULT, /* handleSetCookieRequests= */ false,
defaultRequestProperties, defaultRequestProperties,
/* handleSetCookieRequests= */ false); /* contentTypePredicate= */ null);
} }
/** /** @deprecated Use {@link CronetDataSource.Factory} instead. */
* Creates an instance. @Deprecated
*
* @param cronetEngine A CronetEngine.
* @param executor The {@link java.util.concurrent.Executor} that will handle responses. This may
* be a direct executor (i.e. executes tasks on the calling thread) in order to avoid a thread
* hop from Cronet's internal network thread to the response handling thread. However, to
* avoid slowing down overall network performance, care must be taken to make sure response
* handling is a fast operation when using a direct executor.
* @param connectTimeoutMs The connection timeout, in milliseconds.
* @param readTimeoutMs The read timeout, in milliseconds.
* @param resetTimeoutOnRedirects Whether the connect timeout is reset when a redirect occurs.
* @param defaultRequestProperties Optional default {@link RequestProperties} to be sent to the
* server as HTTP headers on every request.
* @param handleSetCookieRequests Whether "Set-Cookie" requests on redirect should be forwarded to
* the redirect url in the "Cookie" header.
*/
public CronetDataSource( public CronetDataSource(
CronetEngine cronetEngine, CronetEngine cronetEngine,
Executor executor, Executor executor,
@ -233,26 +372,12 @@ public class CronetDataSource extends BaseDataSource implements HttpDataSource {
connectTimeoutMs, connectTimeoutMs,
readTimeoutMs, readTimeoutMs,
resetTimeoutOnRedirects, resetTimeoutOnRedirects,
Clock.DEFAULT, handleSetCookieRequests,
defaultRequestProperties, defaultRequestProperties,
handleSetCookieRequests); /* contentTypePredicate= */ null);
} }
/** /** @deprecated Use {@link CronetDataSource.Factory} instead. */
* Creates an instance.
*
* @param cronetEngine A CronetEngine.
* @param executor The {@link java.util.concurrent.Executor} that will handle responses. This may
* be a direct executor (i.e. executes tasks on the calling thread) in order to avoid a thread
* hop from Cronet's internal network thread to the response handling thread. However, to
* avoid slowing down overall network performance, care must be taken to make sure response
* handling is a fast operation when using a direct executor.
* @param contentTypePredicate An optional {@link Predicate}. If a content type is rejected by the
* predicate then an {@link InvalidContentTypeException} is thrown from {@link
* #open(DataSpec)}.
* @deprecated Use {@link #CronetDataSource(CronetEngine, Executor)} and {@link
* #setContentTypePredicate(Predicate)}.
*/
@SuppressWarnings("deprecation") @SuppressWarnings("deprecation")
@Deprecated @Deprecated
public CronetDataSource( public CronetDataSource(
@ -269,26 +394,7 @@ public class CronetDataSource extends BaseDataSource implements HttpDataSource {
/* defaultRequestProperties= */ null); /* defaultRequestProperties= */ null);
} }
/** /** @deprecated Use {@link CronetDataSource.Factory} instead. */
* Creates an instance.
*
* @param cronetEngine A CronetEngine.
* @param executor The {@link java.util.concurrent.Executor} that will handle responses. This may
* be a direct executor (i.e. executes tasks on the calling thread) in order to avoid a thread
* hop from Cronet's internal network thread to the response handling thread. However, to
* avoid slowing down overall network performance, care must be taken to make sure response
* handling is a fast operation when using a direct executor.
* @param contentTypePredicate An optional {@link Predicate}. If a content type is rejected by the
* predicate then an {@link InvalidContentTypeException} is thrown from {@link
* #open(DataSpec)}.
* @param connectTimeoutMs The connection timeout, in milliseconds.
* @param readTimeoutMs The read timeout, in milliseconds.
* @param resetTimeoutOnRedirects Whether the connect timeout is reset when a redirect occurs.
* @param defaultRequestProperties Optional default {@link RequestProperties} to be sent to the
* server as HTTP headers on every request.
* @deprecated Use {@link #CronetDataSource(CronetEngine, Executor, int, int, boolean,
* RequestProperties)} and {@link #setContentTypePredicate(Predicate)}.
*/
@SuppressWarnings("deprecation") @SuppressWarnings("deprecation")
@Deprecated @Deprecated
public CronetDataSource( public CronetDataSource(
@ -310,28 +416,7 @@ public class CronetDataSource extends BaseDataSource implements HttpDataSource {
/* handleSetCookieRequests= */ false); /* handleSetCookieRequests= */ false);
} }
/** /** @deprecated Use {@link CronetDataSource.Factory} instead. */
* Creates an instance.
*
* @param cronetEngine A CronetEngine.
* @param executor The {@link java.util.concurrent.Executor} that will handle responses. This may
* be a direct executor (i.e. executes tasks on the calling thread) in order to avoid a thread
* hop from Cronet's internal network thread to the response handling thread. However, to
* avoid slowing down overall network performance, care must be taken to make sure response
* handling is a fast operation when using a direct executor.
* @param contentTypePredicate An optional {@link Predicate}. If a content type is rejected by the
* predicate then an {@link InvalidContentTypeException} is thrown from {@link
* #open(DataSpec)}.
* @param connectTimeoutMs The connection timeout, in milliseconds.
* @param readTimeoutMs The read timeout, in milliseconds.
* @param resetTimeoutOnRedirects Whether the connect timeout is reset when a redirect occurs.
* @param defaultRequestProperties Optional default {@link RequestProperties} to be sent to the
* server as HTTP headers on every request.
* @param handleSetCookieRequests Whether "Set-Cookie" requests on redirect should be forwarded to
* the redirect url in the "Cookie" header.
* @deprecated Use {@link #CronetDataSource(CronetEngine, Executor, int, int, boolean,
* RequestProperties, boolean)} and {@link #setContentTypePredicate(Predicate)}.
*/
@Deprecated @Deprecated
public CronetDataSource( public CronetDataSource(
CronetEngine cronetEngine, CronetEngine cronetEngine,
@ -348,31 +433,31 @@ public class CronetDataSource extends BaseDataSource implements HttpDataSource {
connectTimeoutMs, connectTimeoutMs,
readTimeoutMs, readTimeoutMs,
resetTimeoutOnRedirects, resetTimeoutOnRedirects,
Clock.DEFAULT, handleSetCookieRequests,
defaultRequestProperties, defaultRequestProperties,
handleSetCookieRequests); contentTypePredicate);
this.contentTypePredicate = contentTypePredicate;
} }
/* package */ CronetDataSource( private CronetDataSource(
CronetEngine cronetEngine, CronetEngine cronetEngine,
Executor executor, Executor executor,
int connectTimeoutMs, int connectTimeoutMs,
int readTimeoutMs, int readTimeoutMs,
boolean resetTimeoutOnRedirects, boolean resetTimeoutOnRedirects,
Clock clock, boolean handleSetCookieRequests,
@Nullable RequestProperties defaultRequestProperties, @Nullable RequestProperties defaultRequestProperties,
boolean handleSetCookieRequests) { @Nullable Predicate<String> contentTypePredicate) {
super(/* isNetwork= */ true); super(/* isNetwork= */ true);
this.urlRequestCallback = new UrlRequestCallback();
this.cronetEngine = Assertions.checkNotNull(cronetEngine); this.cronetEngine = Assertions.checkNotNull(cronetEngine);
this.executor = Assertions.checkNotNull(executor); this.executor = Assertions.checkNotNull(executor);
this.connectTimeoutMs = connectTimeoutMs; this.connectTimeoutMs = connectTimeoutMs;
this.readTimeoutMs = readTimeoutMs; this.readTimeoutMs = readTimeoutMs;
this.resetTimeoutOnRedirects = resetTimeoutOnRedirects; this.resetTimeoutOnRedirects = resetTimeoutOnRedirects;
this.clock = Assertions.checkNotNull(clock);
this.defaultRequestProperties = defaultRequestProperties; this.defaultRequestProperties = defaultRequestProperties;
this.handleSetCookieRequests = handleSetCookieRequests; this.handleSetCookieRequests = handleSetCookieRequests;
this.contentTypePredicate = contentTypePredicate;
clock = Clock.DEFAULT;
urlRequestCallback = new UrlRequestCallback();
requestProperties = new RequestProperties(); requestProperties = new RequestProperties();
operation = new ConditionVariable(); operation = new ConditionVariable();
} }
@ -384,6 +469,7 @@ public class CronetDataSource extends BaseDataSource implements HttpDataSource {
* @param contentTypePredicate The content type {@link Predicate}, or {@code null} to clear a * @param contentTypePredicate The content type {@link Predicate}, or {@code null} to clear a
* predicate that was previously set. * predicate that was previously set.
*/ */
@Deprecated
public void setContentTypePredicate(@Nullable Predicate<String> contentTypePredicate) { public void setContentTypePredicate(@Nullable Predicate<String> contentTypePredicate) {
this.contentTypePredicate = contentTypePredicate; this.contentTypePredicate = contentTypePredicate;
} }

View File

@ -21,14 +21,14 @@ import androidx.annotation.Nullable;
import com.google.android.exoplayer2.upstream.DefaultHttpDataSourceFactory; import com.google.android.exoplayer2.upstream.DefaultHttpDataSourceFactory;
import com.google.android.exoplayer2.upstream.HttpDataSource; import com.google.android.exoplayer2.upstream.HttpDataSource;
import com.google.android.exoplayer2.upstream.HttpDataSource.BaseFactory; import com.google.android.exoplayer2.upstream.HttpDataSource.BaseFactory;
import com.google.android.exoplayer2.upstream.HttpDataSource.Factory;
import com.google.android.exoplayer2.upstream.TransferListener; import com.google.android.exoplayer2.upstream.TransferListener;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
import org.chromium.net.CronetEngine; import org.chromium.net.CronetEngine;
/** /** @deprecated Use {@link CronetDataSource.Factory} instead. */
* A {@link Factory} that produces {@link CronetDataSource}. // Uses deprecated DefaultHttpDataSourceFactory
*/ @SuppressWarnings("deprecation")
@Deprecated
public final class CronetDataSourceFactory extends BaseFactory { public final class CronetDataSourceFactory extends BaseFactory {
/** /**

View File

@ -17,6 +17,7 @@ package com.google.android.exoplayer2.ext.cronet;
import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertThat;
import static java.lang.Math.min; import static java.lang.Math.min;
import static java.util.concurrent.TimeUnit.SECONDS;
import static org.junit.Assert.fail; import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.anyString;
@ -32,13 +33,15 @@ import static org.mockito.Mockito.when;
import android.net.Uri; import android.net.Uri;
import android.os.ConditionVariable; import android.os.ConditionVariable;
import android.os.SystemClock; import android.os.SystemClock;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.ExoPlayerLibraryInfo;
import com.google.android.exoplayer2.upstream.DataSpec; import com.google.android.exoplayer2.upstream.DataSpec;
import com.google.android.exoplayer2.upstream.DefaultHttpDataSource;
import com.google.android.exoplayer2.upstream.HttpDataSource; import com.google.android.exoplayer2.upstream.HttpDataSource;
import com.google.android.exoplayer2.upstream.HttpDataSource.HttpDataSourceException; import com.google.android.exoplayer2.upstream.HttpDataSource.HttpDataSourceException;
import com.google.android.exoplayer2.upstream.TransferListener; import com.google.android.exoplayer2.upstream.TransferListener;
import com.google.android.exoplayer2.util.Clock;
import com.google.android.exoplayer2.util.Util; import com.google.android.exoplayer2.util.Util;
import java.io.IOException; import java.io.IOException;
import java.io.InterruptedIOException; import java.io.InterruptedIOException;
@ -53,8 +56,12 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.concurrent.CountDownLatch; import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.atomic.AtomicReference;
import okhttp3.Headers;
import okhttp3.mockwebserver.MockResponse;
import okhttp3.mockwebserver.MockWebServer;
import org.chromium.net.CronetEngine; import org.chromium.net.CronetEngine;
import org.chromium.net.NetworkException; import org.chromium.net.NetworkException;
import org.chromium.net.UrlRequest; import org.chromium.net.UrlRequest;
@ -100,21 +107,18 @@ public final class CronetDataSourceTest {
public void setUp() { public void setUp() {
MockitoAnnotations.initMocks(this); MockitoAnnotations.initMocks(this);
HttpDataSource.RequestProperties defaultRequestProperties = Map<String, String> defaultRequestProperties = new HashMap<>();
new HttpDataSource.RequestProperties(); defaultRequestProperties.put("defaultHeader1", "defaultValue1");
defaultRequestProperties.set("defaultHeader1", "defaultValue1"); defaultRequestProperties.put("defaultHeader2", "defaultValue2");
defaultRequestProperties.set("defaultHeader2", "defaultValue2");
dataSourceUnderTest = dataSourceUnderTest =
new CronetDataSource( (CronetDataSource)
mockCronetEngine, new CronetDataSource.Factory(new CronetEngineWrapper(mockCronetEngine), mockExecutor)
mockExecutor, .setConnectionTimeoutMs(TEST_CONNECT_TIMEOUT_MS)
TEST_CONNECT_TIMEOUT_MS, .setReadTimeoutMs(TEST_READ_TIMEOUT_MS)
TEST_READ_TIMEOUT_MS, .setResetTimeoutOnRedirects(true)
/* resetTimeoutOnRedirects= */ true, .setDefaultRequestProperties(defaultRequestProperties)
Clock.DEFAULT, .createDataSource();
defaultRequestProperties,
/* handleSetCookieRequests= */ false);
dataSourceUnderTest.addTransferListener(mockTransferListener); dataSourceUnderTest.addTransferListener(mockTransferListener);
when(mockCronetEngine.newUrlRequestBuilder( when(mockCronetEngine.newUrlRequestBuilder(
anyString(), any(UrlRequest.Callback.class), any(Executor.class))) anyString(), any(UrlRequest.Callback.class), any(Executor.class)))
@ -1134,15 +1138,13 @@ public final class CronetDataSourceTest {
testRedirectParseAndAttachCookie_dataSourceHandlesSetCookie_andPreservesOriginalRequestHeaders() testRedirectParseAndAttachCookie_dataSourceHandlesSetCookie_andPreservesOriginalRequestHeaders()
throws HttpDataSourceException { throws HttpDataSourceException {
dataSourceUnderTest = dataSourceUnderTest =
new CronetDataSource( (CronetDataSource)
mockCronetEngine, new CronetDataSource.Factory(new CronetEngineWrapper(mockCronetEngine), mockExecutor)
mockExecutor, .setConnectionTimeoutMs(TEST_CONNECT_TIMEOUT_MS)
TEST_CONNECT_TIMEOUT_MS, .setReadTimeoutMs(TEST_READ_TIMEOUT_MS)
TEST_READ_TIMEOUT_MS, .setResetTimeoutOnRedirects(true)
true, // resetTimeoutOnRedirects .setHandleSetCookieRequests(true)
Clock.DEFAULT, .createDataSource();
null,
true);
dataSourceUnderTest.addTransferListener(mockTransferListener); dataSourceUnderTest.addTransferListener(mockTransferListener);
dataSourceUnderTest.setRequestProperty("Content-Type", TEST_CONTENT_TYPE); dataSourceUnderTest.setRequestProperty("Content-Type", TEST_CONTENT_TYPE);
@ -1164,15 +1166,13 @@ public final class CronetDataSourceTest {
throws HttpDataSourceException { throws HttpDataSourceException {
testDataSpec = new DataSpec(Uri.parse(TEST_URL), 1000, 5000); testDataSpec = new DataSpec(Uri.parse(TEST_URL), 1000, 5000);
dataSourceUnderTest = dataSourceUnderTest =
new CronetDataSource( (CronetDataSource)
mockCronetEngine, new CronetDataSource.Factory(new CronetEngineWrapper(mockCronetEngine), mockExecutor)
mockExecutor, .setConnectionTimeoutMs(TEST_CONNECT_TIMEOUT_MS)
TEST_CONNECT_TIMEOUT_MS, .setReadTimeoutMs(TEST_READ_TIMEOUT_MS)
TEST_READ_TIMEOUT_MS, .setResetTimeoutOnRedirects(true)
/* resetTimeoutOnRedirects= */ true, .setHandleSetCookieRequests(true)
Clock.DEFAULT, .createDataSource();
/* defaultRequestProperties= */ null,
/* handleSetCookieRequests= */ true);
dataSourceUnderTest.addTransferListener(mockTransferListener); dataSourceUnderTest.addTransferListener(mockTransferListener);
dataSourceUnderTest.setRequestProperty("Content-Type", TEST_CONTENT_TYPE); dataSourceUnderTest.setRequestProperty("Content-Type", TEST_CONTENT_TYPE);
@ -1202,15 +1202,13 @@ public final class CronetDataSourceTest {
public void redirectNoSetCookieFollowsRedirect_dataSourceHandlesSetCookie() public void redirectNoSetCookieFollowsRedirect_dataSourceHandlesSetCookie()
throws HttpDataSourceException { throws HttpDataSourceException {
dataSourceUnderTest = dataSourceUnderTest =
new CronetDataSource( (CronetDataSource)
mockCronetEngine, new CronetDataSource.Factory(new CronetEngineWrapper(mockCronetEngine), mockExecutor)
mockExecutor, .setConnectionTimeoutMs(TEST_CONNECT_TIMEOUT_MS)
TEST_CONNECT_TIMEOUT_MS, .setReadTimeoutMs(TEST_READ_TIMEOUT_MS)
TEST_READ_TIMEOUT_MS, .setResetTimeoutOnRedirects(true)
/* resetTimeoutOnRedirects= */ true, .setHandleSetCookieRequests(true)
Clock.DEFAULT, .createDataSource();
/* defaultRequestProperties= */ null,
/* handleSetCookieRequests= */ true);
dataSourceUnderTest.addTransferListener(mockTransferListener); dataSourceUnderTest.addTransferListener(mockTransferListener);
mockSingleRedirectSuccess(); mockSingleRedirectSuccess();
mockFollowRedirectSuccess(); mockFollowRedirectSuccess();
@ -1356,6 +1354,99 @@ public final class CronetDataSourceTest {
verify(mockUrlRequestBuilder).allowDirectExecutor(); verify(mockUrlRequestBuilder).allowDirectExecutor();
} }
@Test
public void factorySetFallbackHttpDataSourceFactory_cronetNotAvailable_usesFallbackFactory()
throws HttpDataSourceException, InterruptedException {
MockWebServer mockWebServer = new MockWebServer();
mockWebServer.enqueue(new MockResponse());
CronetEngineWrapper cronetEngineWrapper = new CronetEngineWrapper((CronetEngine) null);
DefaultHttpDataSource.Factory fallbackFactory =
new DefaultHttpDataSource.Factory().setUserAgent("customFallbackFactoryUserAgent");
HttpDataSource dataSourceUnderTest =
new CronetDataSource.Factory(cronetEngineWrapper, Executors.newSingleThreadExecutor())
.setFallbackFactory(fallbackFactory)
.createDataSource();
dataSourceUnderTest.open(
new DataSpec.Builder().setUri(mockWebServer.url("/test-path").toString()).build());
Headers headers = mockWebServer.takeRequest(10, SECONDS).getHeaders();
assertThat(headers.get("user-agent")).isEqualTo("customFallbackFactoryUserAgent");
}
@Test
public void
factory_noFallbackFactoryCronetNotAvailable_delegateTransferListenerToInternalFallbackFactory()
throws HttpDataSourceException, InterruptedException {
MockWebServer mockWebServer = new MockWebServer();
mockWebServer.enqueue(new MockResponse());
CronetEngineWrapper cronetEngineWrapper = new CronetEngineWrapper((CronetEngine) null);
HttpDataSource dataSourceUnderTest =
new CronetDataSource.Factory(cronetEngineWrapper, Executors.newSingleThreadExecutor())
.setTransferListener(mockTransferListener)
.createDataSource();
DataSpec dataSpec =
new DataSpec.Builder().setUri(mockWebServer.url("/test-path").toString()).build();
dataSourceUnderTest.open(dataSpec);
Headers headers = mockWebServer.takeRequest(10, SECONDS).getHeaders();
assertThat(headers.get("user-agent")).isEqualTo(ExoPlayerLibraryInfo.DEFAULT_USER_AGENT);
verify(mockTransferListener)
.onTransferInitializing(eq(dataSourceUnderTest), eq(dataSpec), /* isNetwork= */ eq(true));
verify(mockTransferListener)
.onTransferStart(eq(dataSourceUnderTest), eq(dataSpec), /* isNetwork= */ eq(true));
}
@Test
public void
factory_noFallbackFactoryCronetNotAvailable_delegateDefaultRequestPropertiesToInternalFallbackFactory()
throws HttpDataSourceException, InterruptedException {
MockWebServer mockWebServer = new MockWebServer();
mockWebServer.enqueue(new MockResponse());
CronetEngineWrapper cronetEngineWrapper =
new CronetEngineWrapper(ApplicationProvider.getApplicationContext());
Map<String, String> defaultRequestProperties = new HashMap<>();
defaultRequestProperties.put("0", "defaultRequestProperty0");
HttpDataSource dataSourceUnderTest =
new CronetDataSource.Factory(cronetEngineWrapper, Executors.newSingleThreadExecutor())
.setDefaultRequestProperties(defaultRequestProperties)
.createDataSource();
dataSourceUnderTest.open(
new DataSpec.Builder().setUri(mockWebServer.url("/test-path").toString()).build());
Headers headers = mockWebServer.takeRequest(10, SECONDS).getHeaders();
assertThat(headers.get("0")).isEqualTo("defaultRequestProperty0");
assertThat(dataSourceUnderTest).isInstanceOf(DefaultHttpDataSource.class);
}
@Test
public void
factory_noFallbackFactoryCronetNotAvailable_delegateDefaultRequestPropertiesToInternalFallbackFactoryAfterCreation()
throws HttpDataSourceException, InterruptedException {
MockWebServer mockWebServer = new MockWebServer();
mockWebServer.enqueue(new MockResponse());
CronetEngineWrapper cronetEngineWrapper = new CronetEngineWrapper((CronetEngine) null);
Map<String, String> defaultRequestProperties = new HashMap<>();
defaultRequestProperties.put("0", "defaultRequestProperty0");
CronetDataSource.Factory factory =
new CronetDataSource.Factory(cronetEngineWrapper, Executors.newSingleThreadExecutor());
HttpDataSource dataSourceUnderTest =
factory.setDefaultRequestProperties(defaultRequestProperties).createDataSource();
defaultRequestProperties.clear();
defaultRequestProperties.put("1", "defaultRequestPropertyAfterCreation");
factory.setDefaultRequestProperties(defaultRequestProperties);
dataSourceUnderTest.open(
new DataSpec.Builder().setUri(mockWebServer.url("/test-path").toString()).build());
Headers headers = mockWebServer.takeRequest(10, SECONDS).getHeaders();
assertThat(headers.get("0")).isNull();
assertThat(headers.get("1")).isEqualTo("defaultRequestPropertyAfterCreation");
assertThat(dataSourceUnderTest).isInstanceOf(DefaultHttpDataSource.class);
}
// Helper methods. // Helper methods.
private void mockStatusResponse() { private void mockStatusResponse() {