Improve user-agent configuration

- Support setting the user-agent in CronetDataSource
- Support setting the default user-agent in CronetEngineWrapper
- Use the underlying network stack's default user-agent by
  default. Many applications will configure the underlying
  CronetEngine or OkHttpClient with a user-agent that they
  expect to be used throughout their app, so always overriding
  this with our own default, on reflection, is not the best
  thing to do!

Issue: #8395
PiperOrigin-RevId: 350921963
This commit is contained in:
olly 2021-01-09 14:16:51 +00:00 committed by Ian Baker
parent 7e295cbfc9
commit 2a5f6d8f62
14 changed files with 143 additions and 82 deletions

View File

@ -99,6 +99,8 @@
([#1807](https://github.com/google/ExoPlayer/issues/1807)).
* Metadata retriever:
* Parse Google Photos HEIC motion photos metadata.
* Data sources:
* Use the user agent of the underlying network stack by default.
* IMA extension:
* Add support for playback of ads in playlists
([#3750](https://github.com/google/ExoPlayer/issues/3750)).
@ -116,6 +118,8 @@
* Add `OkHttpDataSource.Factory` and deprecate `OkHttpDataSourceFactory`.
* Cronet extension:
* Add `CronetDataSource.Factory` and deprecate `CronetDataSourceFactory`.
* Support setting the user agent on `CronetDataSource.Factory` and
`CronetEngineWrapper`.
* Media2 extension
* Make media2-extension depend on AndroidX media2:media2-session:1.1.0 to
fix a deadlock while creating PlaybackStateCompat internally.

View File

@ -16,7 +16,9 @@
package com.google.android.exoplayer2.demo;
import android.content.Context;
import android.os.Build;
import com.google.android.exoplayer2.DefaultRenderersFactory;
import com.google.android.exoplayer2.ExoPlayerLibraryInfo;
import com.google.android.exoplayer2.RenderersFactory;
import com.google.android.exoplayer2.database.DatabaseProvider;
import com.google.android.exoplayer2.database.ExoDatabaseProvider;
@ -44,6 +46,13 @@ public final class DemoUtil {
public static final String DOWNLOAD_NOTIFICATION_CHANNEL_ID = "download_channel";
private static final String USER_AGENT =
"ExoPlayerDemo/"
+ ExoPlayerLibraryInfo.VERSION
+ " (Linux; Android "
+ Build.VERSION.RELEASE
+ ") "
+ ExoPlayerLibraryInfo.VERSION_SLASHY;
private static final String TAG = "DemoUtil";
private static final String DOWNLOAD_ACTION_FILE = "actions";
private static final String DOWNLOAD_TRACKER_ACTION_FILE = "tracked_actions";
@ -79,7 +88,8 @@ public final class DemoUtil {
public static synchronized HttpDataSource.Factory getHttpDataSourceFactory(Context context) {
if (httpDataSourceFactory == null) {
context = context.getApplicationContext();
CronetEngineWrapper cronetEngineWrapper = new CronetEngineWrapper(context);
CronetEngineWrapper cronetEngineWrapper =
new CronetEngineWrapper(context, USER_AGENT, /* preferGMSCoreCronet= */ false);
httpDataSourceFactory =
new CronetDataSource.Factory(cronetEngineWrapper, Executors.newSingleThreadExecutor());
}

View File

@ -83,6 +83,7 @@ public class CronetDataSource extends BaseDataSource implements HttpDataSource {
@Nullable private HttpDataSource.Factory fallbackFactory;
@Nullable private Predicate<String> contentTypePredicate;
@Nullable private TransferListener transferListener;
@Nullable private String userAgent;
private int connectTimeoutMs;
private int readTimeoutMs;
private boolean resetTimeoutOnRedirects;
@ -121,6 +122,22 @@ public class CronetDataSource extends BaseDataSource implements HttpDataSource {
return this;
}
/**
* Sets the user agent that will be used.
*
* <p>The default is {@code null}, which causes the default user agent of the underlying {@link
* CronetEngine} to be used.
*
* @param userAgent The user agent that will be used, or {@code null} to use the default user
* agent of the underlying {@link CronetEngine}.
* @return This factory.
*/
public Factory setUserAgent(@Nullable String userAgent) {
this.userAgent = userAgent;
internalFallbackFactory.setUserAgent(userAgent);
return this;
}
/**
* Sets the connect timeout, in milliseconds.
*
@ -239,6 +256,7 @@ public class CronetDataSource extends BaseDataSource implements HttpDataSource {
readTimeoutMs,
resetTimeoutOnRedirects,
handleSetCookieRequests,
userAgent,
defaultRequestProperties,
contentTypePredicate);
if (transferListener != null) {
@ -293,6 +311,7 @@ public class CronetDataSource extends BaseDataSource implements HttpDataSource {
private final int readTimeoutMs;
private final boolean resetTimeoutOnRedirects;
private final boolean handleSetCookieRequests;
@Nullable private final String userAgent;
@Nullable private final RequestProperties defaultRequestProperties;
private final RequestProperties requestProperties;
private final ConditionVariable operation;
@ -352,6 +371,7 @@ public class CronetDataSource extends BaseDataSource implements HttpDataSource {
readTimeoutMs,
resetTimeoutOnRedirects,
/* handleSetCookieRequests= */ false,
/* userAgent= */ null,
defaultRequestProperties,
/* contentTypePredicate= */ null);
}
@ -373,6 +393,7 @@ public class CronetDataSource extends BaseDataSource implements HttpDataSource {
readTimeoutMs,
resetTimeoutOnRedirects,
handleSetCookieRequests,
/* userAgent= */ null,
defaultRequestProperties,
/* contentTypePredicate= */ null);
}
@ -434,6 +455,7 @@ public class CronetDataSource extends BaseDataSource implements HttpDataSource {
readTimeoutMs,
resetTimeoutOnRedirects,
handleSetCookieRequests,
/* userAgent= */ null,
defaultRequestProperties,
contentTypePredicate);
}
@ -445,6 +467,7 @@ public class CronetDataSource extends BaseDataSource implements HttpDataSource {
int readTimeoutMs,
boolean resetTimeoutOnRedirects,
boolean handleSetCookieRequests,
@Nullable String userAgent,
@Nullable RequestProperties defaultRequestProperties,
@Nullable Predicate<String> contentTypePredicate) {
super(/* isNetwork= */ true);
@ -453,8 +476,9 @@ public class CronetDataSource extends BaseDataSource implements HttpDataSource {
this.connectTimeoutMs = connectTimeoutMs;
this.readTimeoutMs = readTimeoutMs;
this.resetTimeoutOnRedirects = resetTimeoutOnRedirects;
this.defaultRequestProperties = defaultRequestProperties;
this.handleSetCookieRequests = handleSetCookieRequests;
this.userAgent = userAgent;
this.defaultRequestProperties = defaultRequestProperties;
this.contentTypePredicate = contentTypePredicate;
clock = Clock.DEFAULT;
urlRequestCallback = new UrlRequestCallback();
@ -820,6 +844,9 @@ public class CronetDataSource extends BaseDataSource implements HttpDataSource {
}
requestBuilder.addHeader("Range", rangeValue.toString());
}
if (userAgent != null) {
requestBuilder.addHeader("User-Agent", userAgent);
}
// TODO: Uncomment when https://bugs.chromium.org/p/chromium/issues/detail?id=711810 is fixed
// (adjusting the code as necessary).
// Force identity encoding unless gzip is allowed.

View File

@ -15,7 +15,6 @@
*/
package com.google.android.exoplayer2.ext.cronet;
import static com.google.android.exoplayer2.ExoPlayerLibraryInfo.DEFAULT_USER_AGENT;
import androidx.annotation.Nullable;
import com.google.android.exoplayer2.upstream.DefaultHttpDataSourceFactory;
@ -92,7 +91,7 @@ public final class CronetDataSourceFactory extends BaseFactory {
* @param executor The {@link java.util.concurrent.Executor} that will perform the requests.
*/
public CronetDataSourceFactory(CronetEngineWrapper cronetEngineWrapper, Executor executor) {
this(cronetEngineWrapper, executor, DEFAULT_USER_AGENT);
this(cronetEngineWrapper, executor, /* userAgent= */ (String) null);
}
/**
@ -106,10 +105,12 @@ public final class CronetDataSourceFactory extends BaseFactory {
*
* @param cronetEngineWrapper A {@link CronetEngineWrapper}.
* @param executor The {@link java.util.concurrent.Executor} that will perform the requests.
* @param userAgent A user agent used to create a fallback HttpDataSource if needed.
* @param userAgent The user agent that will be used by the fallback {@link HttpDataSource} if
* needed, or {@code null} for the fallback to use the default user agent of the underlying
* platform.
*/
public CronetDataSourceFactory(
CronetEngineWrapper cronetEngineWrapper, Executor executor, String userAgent) {
CronetEngineWrapper cronetEngineWrapper, Executor executor, @Nullable String userAgent) {
this(
cronetEngineWrapper,
executor,
@ -136,7 +137,9 @@ public final class CronetDataSourceFactory extends BaseFactory {
* @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 userAgent A user agent used to create a fallback HttpDataSource if needed.
* @param userAgent The user agent that will be used by the fallback {@link HttpDataSource} if
* needed, or {@code null} for the fallback to use the default user agent of the underlying
* platform.
*/
public CronetDataSourceFactory(
CronetEngineWrapper cronetEngineWrapper,
@ -144,7 +147,7 @@ public final class CronetDataSourceFactory extends BaseFactory {
int connectTimeoutMs,
int readTimeoutMs,
boolean resetTimeoutOnRedirects,
String userAgent) {
@Nullable String userAgent) {
this(
cronetEngineWrapper,
executor,
@ -238,7 +241,7 @@ public final class CronetDataSourceFactory extends BaseFactory {
CronetEngineWrapper cronetEngineWrapper,
Executor executor,
@Nullable TransferListener transferListener) {
this(cronetEngineWrapper, executor, transferListener, DEFAULT_USER_AGENT);
this(cronetEngineWrapper, executor, transferListener, /* userAgent= */ (String) null);
}
/**
@ -253,13 +256,15 @@ public final class CronetDataSourceFactory extends BaseFactory {
* @param cronetEngineWrapper A {@link CronetEngineWrapper}.
* @param executor The {@link java.util.concurrent.Executor} that will perform the requests.
* @param transferListener An optional listener.
* @param userAgent A user agent used to create a fallback HttpDataSource if needed.
* @param userAgent The user agent that will be used by the fallback {@link HttpDataSource} if
* needed, or {@code null} for the fallback to use the default user agent of the underlying
* platform.
*/
public CronetDataSourceFactory(
CronetEngineWrapper cronetEngineWrapper,
Executor executor,
@Nullable TransferListener transferListener,
String userAgent) {
@Nullable String userAgent) {
this(
cronetEngineWrapper,
executor,
@ -287,7 +292,9 @@ public final class CronetDataSourceFactory extends BaseFactory {
* @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 userAgent A user agent used to create a fallback HttpDataSource if needed.
* @param userAgent The user agent that will be used by the fallback {@link HttpDataSource} if
* needed, or {@code null} for the fallback to use the default user agent of the underlying
* platform.
*/
public CronetDataSourceFactory(
CronetEngineWrapper cronetEngineWrapper,
@ -296,7 +303,7 @@ public final class CronetDataSourceFactory extends BaseFactory {
int connectTimeoutMs,
int readTimeoutMs,
boolean resetTimeoutOnRedirects,
String userAgent) {
@Nullable String userAgent) {
this(
cronetEngineWrapper,
executor,

View File

@ -73,25 +73,29 @@ public final class CronetEngineWrapper {
public static final int SOURCE_UNAVAILABLE = 4;
/**
* Creates a wrapper for a {@link CronetEngine} which automatically selects the most suitable
* {@link CronetProvider}. Sets wrapper to prefer natively bundled Cronet over GMSCore Cronet
* if both are available.
* Creates a wrapper for a {@link CronetEngine} built using the most suitable {@link
* CronetProvider}. When natively bundled Cronet and GMSCore Cronet are both available, the
* natively bundled provider is preferred.
*
* @param context A context.
*/
public CronetEngineWrapper(Context context) {
this(context, false);
this(context, /* userAgent= */ null, /* preferGMSCoreCronet= */ false);
}
/**
* Creates a wrapper for a {@link CronetEngine} which automatically selects the most suitable
* {@link CronetProvider} based on user preference.
* Creates a wrapper for a {@link CronetEngine} built using the most suitable {@link
* CronetProvider}. When natively bundled Cronet and GMSCore Cronet are both available, {@code
* preferGMSCoreCronet} determines which is preferred.
*
* @param context A context.
* @param userAgent A default user agent, or {@code null} to use a default user agent of the
* {@link CronetEngine}.
* @param preferGMSCoreCronet Whether Cronet from GMSCore should be preferred over natively
* bundled Cronet if both are available.
*/
public CronetEngineWrapper(Context context, boolean preferGMSCoreCronet) {
public CronetEngineWrapper(
Context context, @Nullable String userAgent, boolean preferGMSCoreCronet) {
CronetEngine cronetEngine = null;
@CronetEngineSource int cronetEngineSource = SOURCE_UNAVAILABLE;
List<CronetProvider> cronetProviders = new ArrayList<>(CronetProvider.getAllProviders(context));
@ -108,7 +112,11 @@ public final class CronetEngineWrapper {
for (int i = 0; i < cronetProviders.size() && cronetEngine == null; i++) {
String providerName = cronetProviders.get(i).getName();
try {
cronetEngine = cronetProviders.get(i).createBuilder().build();
CronetEngine.Builder cronetEngineBuilder = cronetProviders.get(i).createBuilder();
if (userAgent != null) {
cronetEngineBuilder.setUserAgent(userAgent);
}
cronetEngine = cronetEngineBuilder.build();
if (providerComparator.isNativeProvider(providerName)) {
cronetEngineSource = SOURCE_NATIVE;
} else if (providerComparator.isGMSCoreProvider(providerName)) {
@ -133,9 +141,9 @@ public final class CronetEngineWrapper {
}
/**
* Creates a wrapper for an existing CronetEngine.
* Creates a wrapper for an existing {@link CronetEngine}.
*
* @param cronetEngine An existing CronetEngine.
* @param cronetEngine The CronetEngine to wrap.
*/
public CronetEngineWrapper(CronetEngine cronetEngine) {
this.cronetEngine = cronetEngine;

View File

@ -36,7 +36,6 @@ import android.os.SystemClock;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
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.DefaultHttpDataSource;
import com.google.android.exoplayer2.upstream.HttpDataSource;
@ -1412,8 +1411,6 @@ public final class CronetDataSourceTest {
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)

View File

@ -15,7 +15,6 @@
*/
package com.google.android.exoplayer2.ext.okhttp;
import static com.google.android.exoplayer2.ExoPlayerLibraryInfo.DEFAULT_USER_AGENT;
import static com.google.android.exoplayer2.util.Util.castNonNull;
import static java.lang.Math.min;
@ -83,7 +82,6 @@ public class OkHttpDataSource extends BaseDataSource implements HttpDataSource {
public Factory(Call.Factory callFactory) {
this.callFactory = callFactory;
defaultRequestProperties = new RequestProperties();
userAgent = DEFAULT_USER_AGENT;
}
/** @deprecated Use {@link #setDefaultRequestProperties(Map)} instead. */
@ -102,12 +100,14 @@ public class OkHttpDataSource extends BaseDataSource implements HttpDataSource {
/**
* Sets the user agent that will be used.
*
* <p>The default is {@link ExoPlayerLibraryInfo#DEFAULT_USER_AGENT}.
* <p>The default is {@code null}, which causes the default user agent of the underlying {@link
* OkHttpClient} to be used.
*
* @param userAgent The user agent that will be used.
* @param userAgent The user agent that will be used, or {@code null} to use the default user
* agent of the underlying {@link OkHttpClient}.
* @return This factory.
*/
public Factory setUserAgent(String userAgent) {
public Factory setUserAgent(@Nullable String userAgent) {
this.userAgent = userAgent;
return this;
}
@ -193,7 +193,7 @@ public class OkHttpDataSource extends BaseDataSource implements HttpDataSource {
@SuppressWarnings("deprecation")
@Deprecated
public OkHttpDataSource(Call.Factory callFactory) {
this(callFactory, DEFAULT_USER_AGENT);
this(callFactory, /* userAgent= */ null);
}
/** @deprecated Use {@link OkHttpDataSource.Factory} instead. */

View File

@ -15,7 +15,6 @@
*/
package com.google.android.exoplayer2.ext.okhttp;
import static com.google.android.exoplayer2.ExoPlayerLibraryInfo.DEFAULT_USER_AGENT;
import androidx.annotation.Nullable;
import com.google.android.exoplayer2.upstream.HttpDataSource;
@ -40,7 +39,7 @@ public final class OkHttpDataSourceFactory extends BaseFactory {
* by the sources created by the factory.
*/
public OkHttpDataSourceFactory(Call.Factory callFactory) {
this(callFactory, DEFAULT_USER_AGENT, /* listener= */ null, /* cacheControl= */ null);
this(callFactory, /* userAgent= */ null, /* listener= */ null, /* cacheControl= */ null);
}
/**

View File

@ -46,9 +46,14 @@ public final class ExoPlayerLibraryInfo {
// Intentionally hardcoded. Do not derive from other constants (e.g. VERSION) or vice versa.
public static final int VERSION_INT = 2012002;
/** The default user agent for requests made by the library. */
/**
* The default user agent for requests made by the library.
*
* @deprecated ExoPlayer now uses the user agent of the underlying network stack by default.
*/
@Deprecated
public static final String DEFAULT_USER_AGENT =
VERSION_SLASHY + " (Linux;Android " + Build.VERSION.RELEASE + ") " + VERSION_SLASHY;
VERSION_SLASHY + " (Linux; Android " + Build.VERSION.RELEASE + ") " + VERSION_SLASHY;
/**
* Whether the library was compiled with {@link com.google.android.exoplayer2.util.Assertions}

View File

@ -15,7 +15,6 @@
*/
package com.google.android.exoplayer2.source;
import static com.google.android.exoplayer2.ExoPlayerLibraryInfo.DEFAULT_USER_AGENT;
import static com.google.android.exoplayer2.drm.DefaultDrmSessionManager.MODE_PLAYBACK;
import androidx.annotation.Nullable;
@ -24,6 +23,7 @@ import com.google.android.exoplayer2.drm.DefaultDrmSessionManager;
import com.google.android.exoplayer2.drm.DrmSessionManager;
import com.google.android.exoplayer2.drm.FrameworkMediaDrm;
import com.google.android.exoplayer2.drm.HttpMediaDrmCallback;
import com.google.android.exoplayer2.upstream.DefaultHttpDataSource;
import com.google.android.exoplayer2.upstream.DefaultHttpDataSourceFactory;
import com.google.android.exoplayer2.upstream.HttpDataSource;
import com.google.android.exoplayer2.util.Assertions;
@ -73,7 +73,7 @@ public final class MediaSourceDrmHelper {
HttpDataSource.Factory dataSourceFactory =
drmHttpDataSourceFactory != null
? drmHttpDataSourceFactory
: new DefaultHttpDataSourceFactory(userAgent != null ? userAgent : DEFAULT_USER_AGENT);
: new DefaultHttpDataSource.Factory().setUserAgent(userAgent);
HttpMediaDrmCallback httpDrmCallback =
new HttpMediaDrmCallback(
drmConfiguration.licenseUri == null ? null : drmConfiguration.licenseUri.toString(),

View File

@ -19,7 +19,6 @@ import android.content.ContentResolver;
import android.content.Context;
import android.net.Uri;
import androidx.annotation.Nullable;
import com.google.android.exoplayer2.ExoPlayerLibraryInfo;
import com.google.android.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.Log;
import com.google.android.exoplayer2.util.Util;
@ -89,7 +88,7 @@ public final class DefaultDataSource implements DataSource {
public DefaultDataSource(Context context, boolean allowCrossProtocolRedirects) {
this(
context,
ExoPlayerLibraryInfo.DEFAULT_USER_AGENT,
/* userAgent= */ null,
DefaultHttpDataSource.DEFAULT_CONNECT_TIMEOUT_MILLIS,
DefaultHttpDataSource.DEFAULT_READ_TIMEOUT_MILLIS,
allowCrossProtocolRedirects);
@ -99,11 +98,13 @@ public final class DefaultDataSource implements DataSource {
* Constructs a new instance, optionally configured to follow cross-protocol redirects.
*
* @param context A context.
* @param userAgent The User-Agent to use when requesting remote data.
* @param userAgent The user agent that will be used when requesting remote data, or {@code null}
* to use the default user agent of the underlying platform.
* @param allowCrossProtocolRedirects Whether cross-protocol redirects (i.e. redirects from HTTP
* to HTTPS and vice versa) are enabled when fetching remote data.
*/
public DefaultDataSource(Context context, String userAgent, boolean allowCrossProtocolRedirects) {
public DefaultDataSource(
Context context, @Nullable String userAgent, boolean allowCrossProtocolRedirects) {
this(
context,
userAgent,
@ -116,7 +117,8 @@ public final class DefaultDataSource implements DataSource {
* Constructs a new instance, optionally configured to follow cross-protocol redirects.
*
* @param context A context.
* @param userAgent The User-Agent to use when requesting remote data.
* @param userAgent The user agent that will be used when requesting remote data, or {@code null}
* to use the default user agent of the underlying platform.
* @param connectTimeoutMillis The connection timeout that should be used when requesting remote
* data, in milliseconds. A timeout of zero is interpreted as an infinite timeout.
* @param readTimeoutMillis The read timeout that should be used when requesting remote data, in
@ -126,7 +128,7 @@ public final class DefaultDataSource implements DataSource {
*/
public DefaultDataSource(
Context context,
String userAgent,
@Nullable String userAgent,
int connectTimeoutMillis,
int readTimeoutMillis,
boolean allowCrossProtocolRedirects) {

View File

@ -15,8 +15,6 @@
*/
package com.google.android.exoplayer2.upstream;
import static com.google.android.exoplayer2.ExoPlayerLibraryInfo.DEFAULT_USER_AGENT;
import android.content.Context;
import androidx.annotation.Nullable;
import com.google.android.exoplayer2.upstream.DataSource.Factory;
@ -37,16 +35,17 @@ public final class DefaultDataSourceFactory implements Factory {
* @param context A context.
*/
public DefaultDataSourceFactory(Context context) {
this(context, DEFAULT_USER_AGENT, /* listener= */ null);
this(context, /* userAgent= */ (String) null, /* listener= */ null);
}
/**
* Creates an instance.
*
* @param context A context.
* @param userAgent The User-Agent string that should be used.
* @param userAgent The user agent that will be used when requesting remote data, or {@code null}
* to use the default user agent of the underlying platform.
*/
public DefaultDataSourceFactory(Context context, String userAgent) {
public DefaultDataSourceFactory(Context context, @Nullable String userAgent) {
this(context, userAgent, /* listener= */ null);
}
@ -54,11 +53,12 @@ public final class DefaultDataSourceFactory implements Factory {
* Creates an instance.
*
* @param context A context.
* @param userAgent The User-Agent string that should be used.
* @param userAgent The user agent that will be used when requesting remote data, or {@code null}
* to use the default user agent of the underlying platform.
* @param listener An optional listener.
*/
public DefaultDataSourceFactory(
Context context, String userAgent, @Nullable TransferListener listener) {
Context context, @Nullable String userAgent, @Nullable TransferListener listener) {
this(context, listener, new DefaultHttpDataSource.Factory().setUserAgent(userAgent));
}

View File

@ -15,7 +15,6 @@
*/
package com.google.android.exoplayer2.upstream;
import static com.google.android.exoplayer2.ExoPlayerLibraryInfo.DEFAULT_USER_AGENT;
import static java.lang.Math.max;
import static java.lang.Math.min;
@ -24,7 +23,6 @@ import android.text.TextUtils;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.ExoPlayerLibraryInfo;
import com.google.android.exoplayer2.upstream.DataSpec.HttpMethod;
import com.google.android.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.Log;
@ -69,7 +67,7 @@ public class DefaultHttpDataSource extends BaseDataSource implements HttpDataSou
@Nullable private TransferListener transferListener;
@Nullable private Predicate<String> contentTypePredicate;
private String userAgent;
@Nullable private String userAgent;
private int connectTimeoutMs;
private int readTimeoutMs;
private boolean allowCrossProtocolRedirects;
@ -77,7 +75,6 @@ public class DefaultHttpDataSource extends BaseDataSource implements HttpDataSou
/** Creates an instance. */
public Factory() {
defaultRequestProperties = new RequestProperties();
userAgent = DEFAULT_USER_AGENT;
connectTimeoutMs = DEFAULT_CONNECT_TIMEOUT_MILLIS;
readTimeoutMs = DEFAULT_READ_TIMEOUT_MILLIS;
}
@ -98,12 +95,14 @@ public class DefaultHttpDataSource extends BaseDataSource implements HttpDataSou
/**
* Sets the user agent that will be used.
*
* <p>The default is {@link ExoPlayerLibraryInfo#DEFAULT_USER_AGENT}.
* <p>The default is {@code null}, which causes the default user agent of the underlying
* platform to be used.
*
* @param userAgent The user agent that will be used.
* @param userAgent The user agent that will be used, or {@code null} to use the default user
* agent of the underlying platform.
* @return This factory.
*/
public Factory setUserAgent(String userAgent) {
public Factory setUserAgent(@Nullable String userAgent) {
this.userAgent = userAgent;
return this;
}
@ -214,7 +213,7 @@ public class DefaultHttpDataSource extends BaseDataSource implements HttpDataSou
private final boolean allowCrossProtocolRedirects;
private final int connectTimeoutMillis;
private final int readTimeoutMillis;
private final String userAgent;
@Nullable private final String userAgent;
@Nullable private final RequestProperties defaultRequestProperties;
private final RequestProperties requestProperties;
@ -235,23 +234,21 @@ public class DefaultHttpDataSource extends BaseDataSource implements HttpDataSou
@SuppressWarnings("deprecation")
@Deprecated
public DefaultHttpDataSource() {
this(
ExoPlayerLibraryInfo.DEFAULT_USER_AGENT,
DEFAULT_CONNECT_TIMEOUT_MILLIS,
DEFAULT_READ_TIMEOUT_MILLIS);
this(/* userAgent= */ null, DEFAULT_CONNECT_TIMEOUT_MILLIS, DEFAULT_READ_TIMEOUT_MILLIS);
}
/** @deprecated Use {@link DefaultHttpDataSource.Factory} instead. */
@SuppressWarnings("deprecation")
@Deprecated
public DefaultHttpDataSource(String userAgent) {
public DefaultHttpDataSource(@Nullable String userAgent) {
this(userAgent, DEFAULT_CONNECT_TIMEOUT_MILLIS, DEFAULT_READ_TIMEOUT_MILLIS);
}
/** @deprecated Use {@link DefaultHttpDataSource.Factory} instead. */
@SuppressWarnings("deprecation")
@Deprecated
public DefaultHttpDataSource(String userAgent, int connectTimeoutMillis, int readTimeoutMillis) {
public DefaultHttpDataSource(
@Nullable String userAgent, int connectTimeoutMillis, int readTimeoutMillis) {
this(
userAgent,
connectTimeoutMillis,
@ -263,7 +260,7 @@ public class DefaultHttpDataSource extends BaseDataSource implements HttpDataSou
/** @deprecated Use {@link DefaultHttpDataSource.Factory} instead. */
@Deprecated
public DefaultHttpDataSource(
String userAgent,
@Nullable String userAgent,
int connectTimeoutMillis,
int readTimeoutMillis,
boolean allowCrossProtocolRedirects,
@ -278,14 +275,14 @@ public class DefaultHttpDataSource extends BaseDataSource implements HttpDataSou
}
private DefaultHttpDataSource(
String userAgent,
@Nullable String userAgent,
int connectTimeoutMillis,
int readTimeoutMillis,
boolean allowCrossProtocolRedirects,
@Nullable RequestProperties defaultRequestProperties,
@Nullable Predicate<String> contentTypePredicate) {
super(/* isNetwork= */ true);
this.userAgent = Assertions.checkNotEmpty(userAgent);
this.userAgent = userAgent;
this.connectTimeoutMillis = connectTimeoutMillis;
this.readTimeoutMillis = readTimeoutMillis;
this.allowCrossProtocolRedirects = allowCrossProtocolRedirects;
@ -620,7 +617,9 @@ public class DefaultHttpDataSource extends BaseDataSource implements HttpDataSou
}
connection.setRequestProperty("Range", rangeRequest);
}
connection.setRequestProperty("User-Agent", userAgent);
if (userAgent != null) {
connection.setRequestProperty("User-Agent", userAgent);
}
connection.setRequestProperty("Accept-Encoding", allowGzip ? "gzip" : "identity");
connection.setInstanceFollowRedirects(followRedirects);
connection.setDoOutput(httpBody != null);

View File

@ -15,17 +15,15 @@
*/
package com.google.android.exoplayer2.upstream;
import static com.google.android.exoplayer2.ExoPlayerLibraryInfo.DEFAULT_USER_AGENT;
import androidx.annotation.Nullable;
import com.google.android.exoplayer2.upstream.HttpDataSource.BaseFactory;
import com.google.android.exoplayer2.util.Assertions;
/** @deprecated Use {@link DefaultHttpDataSource.Factory} instead. */
@Deprecated
public final class DefaultHttpDataSourceFactory extends BaseFactory {
private final String userAgent;
@Nullable private final String userAgent;
@Nullable private final TransferListener listener;
private final int connectTimeoutMillis;
private final int readTimeoutMillis;
@ -37,7 +35,7 @@ public final class DefaultHttpDataSourceFactory extends BaseFactory {
* timeout and disables cross-protocol redirects.
*/
public DefaultHttpDataSourceFactory() {
this(DEFAULT_USER_AGENT);
this(/* userAgent= */ null);
}
/**
@ -45,9 +43,10 @@ public final class DefaultHttpDataSourceFactory extends BaseFactory {
* connection timeout, {@link DefaultHttpDataSource#DEFAULT_READ_TIMEOUT_MILLIS} as the read
* timeout and disables cross-protocol redirects.
*
* @param userAgent The User-Agent string that should be used.
* @param userAgent The user agent that will be used, or {@code null} to use the default user
* agent of the underlying platform.
*/
public DefaultHttpDataSourceFactory(String userAgent) {
public DefaultHttpDataSourceFactory(@Nullable String userAgent) {
this(userAgent, null);
}
@ -56,17 +55,20 @@ public final class DefaultHttpDataSourceFactory extends BaseFactory {
* connection timeout, {@link DefaultHttpDataSource#DEFAULT_READ_TIMEOUT_MILLIS} as the read
* timeout and disables cross-protocol redirects.
*
* @param userAgent The User-Agent string that should be used.
* @param userAgent The user agent that will be used, or {@code null} to use the default user
* agent of the underlying platform.
* @param listener An optional listener.
* @see #DefaultHttpDataSourceFactory(String, TransferListener, int, int, boolean)
*/
public DefaultHttpDataSourceFactory(String userAgent, @Nullable TransferListener listener) {
public DefaultHttpDataSourceFactory(
@Nullable String userAgent, @Nullable TransferListener listener) {
this(userAgent, listener, DefaultHttpDataSource.DEFAULT_CONNECT_TIMEOUT_MILLIS,
DefaultHttpDataSource.DEFAULT_READ_TIMEOUT_MILLIS, false);
}
/**
* @param userAgent The User-Agent string that should be used.
* @param userAgent The user agent that will be used, or {@code null} to use the default user
* agent of the underlying platform.
* @param connectTimeoutMillis The connection timeout that should be used when requesting remote
* data, in milliseconds. A timeout of zero is interpreted as an infinite timeout.
* @param readTimeoutMillis The read timeout that should be used when requesting remote data, in
@ -75,7 +77,7 @@ public final class DefaultHttpDataSourceFactory extends BaseFactory {
* to HTTPS and vice versa) are enabled.
*/
public DefaultHttpDataSourceFactory(
String userAgent,
@Nullable String userAgent,
int connectTimeoutMillis,
int readTimeoutMillis,
boolean allowCrossProtocolRedirects) {
@ -88,7 +90,8 @@ public final class DefaultHttpDataSourceFactory extends BaseFactory {
}
/**
* @param userAgent The User-Agent string that should be used.
* @param userAgent The user agent that will be used, or {@code null} to use the default user
* agent of the underlying platform.
* @param listener An optional listener.
* @param connectTimeoutMillis The connection timeout that should be used when requesting remote
* data, in milliseconds. A timeout of zero is interpreted as an infinite timeout.
@ -98,12 +101,12 @@ public final class DefaultHttpDataSourceFactory extends BaseFactory {
* to HTTPS and vice versa) are enabled.
*/
public DefaultHttpDataSourceFactory(
String userAgent,
@Nullable String userAgent,
@Nullable TransferListener listener,
int connectTimeoutMillis,
int readTimeoutMillis,
boolean allowCrossProtocolRedirects) {
this.userAgent = Assertions.checkNotEmpty(userAgent);
this.userAgent = userAgent;
this.listener = listener;
this.connectTimeoutMillis = connectTimeoutMillis;
this.readTimeoutMillis = readTimeoutMillis;