920 lines
41 KiB
Java
920 lines
41 KiB
Java
![]() |
// Copyright 2015 The Chromium Authors
|
||
|
// Use of this source code is governed by a BSD-style license that can be
|
||
|
// found in the LICENSE file.
|
||
|
|
||
|
package org.chromium.net;
|
||
|
|
||
|
import android.content.Context;
|
||
|
import android.net.http.HttpResponseCache;
|
||
|
import android.util.Log;
|
||
|
|
||
|
import androidx.annotation.VisibleForTesting;
|
||
|
|
||
|
import java.io.IOException;
|
||
|
import java.lang.reflect.Method;
|
||
|
import java.net.URL;
|
||
|
import java.net.URLConnection;
|
||
|
import java.net.URLStreamHandlerFactory;
|
||
|
import java.util.ArrayList;
|
||
|
import java.util.Collections;
|
||
|
import java.util.Comparator;
|
||
|
import java.util.Date;
|
||
|
import java.util.Iterator;
|
||
|
import java.util.List;
|
||
|
import java.util.Set;
|
||
|
import java.util.concurrent.Executor;
|
||
|
|
||
|
import javax.net.ssl.HttpsURLConnection;
|
||
|
|
||
|
/**
|
||
|
* An engine to process {@link UrlRequest}s, which uses the best HTTP stack available on the current
|
||
|
* platform. An instance of this class can be created using {@link Builder}.
|
||
|
*/
|
||
|
public abstract class CronetEngine {
|
||
|
private static final String TAG = CronetEngine.class.getSimpleName();
|
||
|
|
||
|
/** The value of the active request count is unknown */
|
||
|
public static final int ACTIVE_REQUEST_COUNT_UNKNOWN = -1;
|
||
|
|
||
|
/** The value of a connection metric is unknown. */
|
||
|
public static final int CONNECTION_METRIC_UNKNOWN = -1;
|
||
|
|
||
|
/**
|
||
|
* The estimate of the effective connection type is unknown.
|
||
|
*
|
||
|
* @see #getEffectiveConnectionType
|
||
|
*/
|
||
|
public static final int EFFECTIVE_CONNECTION_TYPE_UNKNOWN = 0;
|
||
|
|
||
|
/**
|
||
|
* The device is offline.
|
||
|
*
|
||
|
* @see #getEffectiveConnectionType
|
||
|
*/
|
||
|
public static final int EFFECTIVE_CONNECTION_TYPE_OFFLINE = 1;
|
||
|
|
||
|
/**
|
||
|
* The estimate of the effective connection type is slow 2G.
|
||
|
*
|
||
|
* @see #getEffectiveConnectionType
|
||
|
*/
|
||
|
public static final int EFFECTIVE_CONNECTION_TYPE_SLOW_2G = 2;
|
||
|
|
||
|
/**
|
||
|
* The estimate of the effective connection type is 2G.
|
||
|
*
|
||
|
* @see #getEffectiveConnectionType
|
||
|
*/
|
||
|
public static final int EFFECTIVE_CONNECTION_TYPE_2G = 3;
|
||
|
|
||
|
/**
|
||
|
* The estimate of the effective connection type is 3G.
|
||
|
*
|
||
|
* @see #getEffectiveConnectionType
|
||
|
*/
|
||
|
public static final int EFFECTIVE_CONNECTION_TYPE_3G = 4;
|
||
|
|
||
|
/**
|
||
|
* The estimate of the effective connection type is 4G.
|
||
|
*
|
||
|
* @see #getEffectiveConnectionType
|
||
|
*/
|
||
|
public static final int EFFECTIVE_CONNECTION_TYPE_4G = 5;
|
||
|
|
||
|
/** The value to be used to undo any previous network binding. */
|
||
|
public static final long UNBIND_NETWORK_HANDLE = -1;
|
||
|
|
||
|
/**
|
||
|
* A builder for {@link CronetEngine}s, which allows runtime configuration of {@code
|
||
|
* CronetEngine}. Configuration options are set on the builder and then {@link #build} is called
|
||
|
* to create the {@code CronetEngine}.
|
||
|
*/
|
||
|
// NOTE(kapishnikov): In order to avoid breaking the existing API clients, all future methods
|
||
|
// added to this class and other API classes must have default implementation.
|
||
|
public static class Builder {
|
||
|
private static final String TAG = "CronetEngine.Builder";
|
||
|
|
||
|
/**
|
||
|
* A class which provides a method for loading the cronet native library. Apps needing to
|
||
|
* implement custom library loading logic can inherit from this class and pass an instance
|
||
|
* to
|
||
|
* {@link CronetEngine.Builder#setLibraryLoader}. For example, this might be required to
|
||
|
* work around {@code UnsatisfiedLinkError}s caused by flaky installation on certain older
|
||
|
* devices.
|
||
|
*/
|
||
|
public abstract static class LibraryLoader {
|
||
|
/**
|
||
|
* Loads the native library.
|
||
|
*
|
||
|
* @param libName name of the library to load
|
||
|
*/
|
||
|
public abstract void loadLibrary(String libName);
|
||
|
}
|
||
|
|
||
|
/** Reference to the actual builder implementation. {@hide exclude from JavaDoc}. */
|
||
|
protected final ICronetEngineBuilder mBuilderDelegate;
|
||
|
|
||
|
/**
|
||
|
* Constructs a {@link Builder} object that facilitates creating a {@link CronetEngine}. The
|
||
|
* default configuration enables HTTP/2 and QUIC, but disables the HTTP cache.
|
||
|
*
|
||
|
* @param context Android {@link Context}, which is used by {@link Builder} to retrieve the
|
||
|
* application context. A reference to only the application context will be kept, so as to
|
||
|
* avoid extending the lifetime of {@code context} unnecessarily.
|
||
|
*/
|
||
|
public Builder(Context context) {
|
||
|
this(createBuilderDelegate(context));
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Constructs {@link Builder} with a given delegate that provides the actual implementation
|
||
|
* of the {@code Builder} methods. This constructor is used only by the internal
|
||
|
* implementation.
|
||
|
*
|
||
|
* @param builderDelegate delegate that provides the actual implementation.
|
||
|
* <p>{@hide}
|
||
|
*/
|
||
|
public Builder(ICronetEngineBuilder builderDelegate) {
|
||
|
if (builderDelegate instanceof ExperimentalOptionsTranslatingCronetEngineBuilder) {
|
||
|
// Already wrapped at the top level, no need to do it again
|
||
|
mBuilderDelegate = builderDelegate;
|
||
|
} else {
|
||
|
mBuilderDelegate =
|
||
|
new ExperimentalOptionsTranslatingCronetEngineBuilder(builderDelegate);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Constructs a User-Agent string including application name and version, system build
|
||
|
* version, model and id, and Cronet version.
|
||
|
*
|
||
|
* @return User-Agent string.
|
||
|
*/
|
||
|
public String getDefaultUserAgent() {
|
||
|
return mBuilderDelegate.getDefaultUserAgent();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Overrides the User-Agent header for all requests. An explicitly set User-Agent header
|
||
|
* (set using {@link UrlRequest.Builder#addHeader}) will override a value set using this
|
||
|
* function.
|
||
|
*
|
||
|
* @param userAgent the User-Agent string to use for all requests.
|
||
|
* @return the builder to facilitate chaining.
|
||
|
*/
|
||
|
public Builder setUserAgent(String userAgent) {
|
||
|
mBuilderDelegate.setUserAgent(userAgent);
|
||
|
return this;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Sets directory for HTTP Cache and Cookie Storage. The directory must exist.
|
||
|
*
|
||
|
* <p><b>NOTE:</b> Do not use the same storage directory with more than one {@code
|
||
|
* CronetEngine} at a time. Access to the storage directory does not support concurrent
|
||
|
* access by multiple {@code CronetEngine}s.
|
||
|
*
|
||
|
* @param value path to existing directory.
|
||
|
* @return the builder to facilitate chaining.
|
||
|
*/
|
||
|
public Builder setStoragePath(String value) {
|
||
|
mBuilderDelegate.setStoragePath(value);
|
||
|
return this;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Sets a {@link LibraryLoader} to be used to load the native library. If not set, the
|
||
|
* library will be loaded using {@link System#loadLibrary}.
|
||
|
*
|
||
|
* @param loader {@code LibraryLoader} to be used to load the native library.
|
||
|
* @return the builder to facilitate chaining.
|
||
|
*/
|
||
|
public Builder setLibraryLoader(LibraryLoader loader) {
|
||
|
mBuilderDelegate.setLibraryLoader(loader);
|
||
|
return this;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Sets whether <a href="https://www.chromium.org/quic">QUIC</a> protocol is enabled.
|
||
|
* Defaults to enabled. If QUIC is enabled, then QUIC User Agent Id containing application
|
||
|
* name and Cronet version is sent to the server.
|
||
|
*
|
||
|
* @param value {@code true} to enable QUIC, {@code false} to disable.
|
||
|
* @return the builder to facilitate chaining.
|
||
|
*/
|
||
|
public Builder enableQuic(boolean value) {
|
||
|
mBuilderDelegate.enableQuic(value);
|
||
|
return this;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Sets whether <a href="https://tools.ietf.org/html/rfc7540">HTTP/2</a> protocol is
|
||
|
* enabled. Defaults to enabled.
|
||
|
*
|
||
|
* @param value {@code true} to enable HTTP/2, {@code false} to disable.
|
||
|
* @return the builder to facilitate chaining.
|
||
|
*/
|
||
|
public Builder enableHttp2(boolean value) {
|
||
|
mBuilderDelegate.enableHttp2(value);
|
||
|
return this;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @deprecated SDCH is deprecated in Cronet M63. This method is a no-op. {@hide exclude from
|
||
|
* JavaDoc}.
|
||
|
*/
|
||
|
@Deprecated
|
||
|
public Builder enableSdch(boolean value) {
|
||
|
return this;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Sets whether <a href="https://tools.ietf.org/html/rfc7932">Brotli</a> compression is
|
||
|
* enabled. If enabled, Brotli will be advertised in Accept-Encoding request headers.
|
||
|
* Defaults to disabled.
|
||
|
*
|
||
|
* @param value {@code true} to enable Brotli, {@code false} to disable.
|
||
|
* @return the builder to facilitate chaining.
|
||
|
*/
|
||
|
public Builder enableBrotli(boolean value) {
|
||
|
mBuilderDelegate.enableBrotli(value);
|
||
|
return this;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Setting to disable HTTP cache. Some data may still be temporarily stored in memory.
|
||
|
* Passed to
|
||
|
* {@link #enableHttpCache}.
|
||
|
*/
|
||
|
public static final int HTTP_CACHE_DISABLED = 0;
|
||
|
|
||
|
/**
|
||
|
* Setting to enable in-memory HTTP cache, including HTTP data. Passed to {@link
|
||
|
* #enableHttpCache}.
|
||
|
*/
|
||
|
public static final int HTTP_CACHE_IN_MEMORY = 1;
|
||
|
|
||
|
/**
|
||
|
* Setting to enable on-disk cache, excluding HTTP data. {@link #setStoragePath} must be
|
||
|
* called prior to passing this constant to {@link #enableHttpCache}.
|
||
|
*/
|
||
|
public static final int HTTP_CACHE_DISK_NO_HTTP = 2;
|
||
|
|
||
|
/**
|
||
|
* Setting to enable on-disk cache, including HTTP data. {@link #setStoragePath} must be
|
||
|
* called prior to passing this constant to {@link #enableHttpCache}.
|
||
|
*/
|
||
|
public static final int HTTP_CACHE_DISK = 3;
|
||
|
|
||
|
/**
|
||
|
* Enables or disables caching of HTTP data and other information like QUIC server
|
||
|
* information.
|
||
|
*
|
||
|
* @param cacheMode control location and type of cached data. Must be one of {@link
|
||
|
* #HTTP_CACHE_DISABLED HTTP_CACHE_*}.
|
||
|
* @param maxSize maximum size in bytes used to cache data (advisory and maybe exceeded at
|
||
|
* times).
|
||
|
* @return the builder to facilitate chaining.
|
||
|
*/
|
||
|
public Builder enableHttpCache(int cacheMode, long maxSize) {
|
||
|
mBuilderDelegate.enableHttpCache(cacheMode, maxSize);
|
||
|
return this;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Adds hint that {@code host} supports QUIC. Note that {@link #enableHttpCache
|
||
|
* enableHttpCache}
|
||
|
* ({@link #HTTP_CACHE_DISK}) is needed to take advantage of 0-RTT connection establishment
|
||
|
* between sessions.
|
||
|
*
|
||
|
* @param host hostname of the server that supports QUIC.
|
||
|
* @param port host of the server that supports QUIC.
|
||
|
* @param alternatePort alternate port to use for QUIC.
|
||
|
* @return the builder to facilitate chaining.
|
||
|
*/
|
||
|
public Builder addQuicHint(String host, int port, int alternatePort) {
|
||
|
mBuilderDelegate.addQuicHint(host, port, alternatePort);
|
||
|
return this;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Pins a set of public keys for a given host. By pinning a set of public keys, {@code
|
||
|
* pinsSha256}, communication with {@code hostName} is required to authenticate with a
|
||
|
* certificate with a public key from the set of pinned ones. An app can pin the public key
|
||
|
* of the root certificate, any of the intermediate certificates or the end-entry
|
||
|
* certificate. Authentication will fail and secure communication will not be established if
|
||
|
* none of the public keys is present in the host's certificate chain, even if the host
|
||
|
* attempts to authenticate with a certificate allowed by the device's trusted store of
|
||
|
* certificates.
|
||
|
*
|
||
|
* <p>Calling this method multiple times with the same host name overrides the previously
|
||
|
* set pins for the host.
|
||
|
*
|
||
|
* <p>More information about the public key pinning can be found in <a
|
||
|
* href="https://tools.ietf.org/html/rfc7469">RFC 7469</a>.
|
||
|
*
|
||
|
* @param hostName name of the host to which the public keys should be pinned. A host that
|
||
|
* consists only of digits and the dot character is treated as invalid.
|
||
|
* @param pinsSha256 a set of pins. Each pin is the SHA-256 cryptographic hash of the
|
||
|
* DER-encoded ASN.1 representation of the Subject Public Key Info (SPKI) of the host's
|
||
|
* X.509 certificate. Use {@link java.security.cert.Certificate#getPublicKey()
|
||
|
* Certificate.getPublicKey()} and {@link java.security.Key#getEncoded() Key.getEncoded()}
|
||
|
* to obtain DER-encoded ASN.1 representation of the SPKI. Although, the method does not
|
||
|
* mandate the presence of the backup pin that can be used if the control of the primary
|
||
|
* private key has been lost, it is highly recommended to supply one.
|
||
|
* @param includeSubdomains indicates whether the pinning policy should be applied to
|
||
|
* subdomains
|
||
|
* of {@code hostName}.
|
||
|
* @param expirationDate specifies the expiration date for the pins.
|
||
|
* @return the builder to facilitate chaining.
|
||
|
* @throws NullPointerException if any of the input parameters are {@code null}.
|
||
|
* @throws IllegalArgumentException if the given host name is invalid or {@code pinsSha256}
|
||
|
* contains a byte array that does not represent a valid SHA-256 hash.
|
||
|
*/
|
||
|
public Builder addPublicKeyPins(
|
||
|
String hostName,
|
||
|
Set<byte[]> pinsSha256,
|
||
|
boolean includeSubdomains,
|
||
|
Date expirationDate) {
|
||
|
mBuilderDelegate.addPublicKeyPins(
|
||
|
hostName, pinsSha256, includeSubdomains, expirationDate);
|
||
|
return this;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Enables or disables public key pinning bypass for local trust anchors. Disabling the
|
||
|
* bypass for local trust anchors is highly discouraged since it may prohibit the app from
|
||
|
* communicating with the pinned hosts. E.g., a user may want to send all traffic through an
|
||
|
* SSL enabled proxy by changing the device proxy settings and adding the proxy certificate
|
||
|
* to the list of local trust anchor. Disabling the bypass will most likely prevent the app
|
||
|
* from sending any traffic to the pinned hosts. For more information see 'How does key
|
||
|
* pinning interact with local proxies and filters?' at
|
||
|
* https://www.chromium.org/Home/chromium-security/security-faq
|
||
|
*
|
||
|
* @param value {@code true} to enable the bypass, {@code false} to disable.
|
||
|
* @return the builder to facilitate chaining.
|
||
|
*/
|
||
|
public Builder enablePublicKeyPinningBypassForLocalTrustAnchors(boolean value) {
|
||
|
mBuilderDelegate.enablePublicKeyPinningBypassForLocalTrustAnchors(value);
|
||
|
return this;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Sets the thread priority of Cronet's internal thread.
|
||
|
*
|
||
|
* @param priority the thread priority of Cronet's internal thread. A Linux priority level,
|
||
|
* from
|
||
|
* -20 for highest scheduling priority to 19 for lowest scheduling priority. For more
|
||
|
* information on values, see {@link android.os.Process#setThreadPriority(int, int)} and
|
||
|
* {@link android.os.Process#THREAD_PRIORITY_DEFAULT THREAD_PRIORITY_*} values.
|
||
|
* @return the builder to facilitate chaining.
|
||
|
*/
|
||
|
public Builder setThreadPriority(int priority) {
|
||
|
mBuilderDelegate.setThreadPriority(priority);
|
||
|
return this;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Enables the network quality estimator, which collects and reports measurements of round
|
||
|
* trip time (RTT) and downstream throughput at various layers of the network stack. After
|
||
|
* enabling the estimator, listeners of RTT and throughput can be added with {@link
|
||
|
* #addRttListener} and
|
||
|
* {@link #addThroughputListener} and removed with {@link #removeRttListener} and {@link
|
||
|
* #removeThroughputListener}. The estimator uses memory and CPU only when enabled.
|
||
|
*
|
||
|
* @param value {@code true} to enable network quality estimator, {@code false} to disable.
|
||
|
* @return the builder to facilitate chaining.
|
||
|
*/
|
||
|
public Builder enableNetworkQualityEstimator(boolean value) {
|
||
|
mBuilderDelegate.enableNetworkQualityEstimator(value);
|
||
|
return this;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Configures the behavior of Cronet when using QUIC. For more details, see documentation
|
||
|
* of {@link QuicOptions} and the individual methods of {@link QuicOptions.Builder}.
|
||
|
*
|
||
|
* <p>Only relevant if {@link #enableQuic(boolean)} is enabled.
|
||
|
*
|
||
|
* @return the builder to facilitate chaining.
|
||
|
*/
|
||
|
@QuicOptions.Experimental
|
||
|
public Builder setQuicOptions(QuicOptions quicOptions) {
|
||
|
mBuilderDelegate.setQuicOptions(quicOptions);
|
||
|
return this;
|
||
|
}
|
||
|
|
||
|
/** @see #setQuicOptions(QuicOptions) */
|
||
|
@QuicOptions.Experimental
|
||
|
public Builder setQuicOptions(QuicOptions.Builder quicOptionsBuilder) {
|
||
|
return setQuicOptions(quicOptionsBuilder.build());
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Configures the behavior of hostname lookup. For more details, see documentation
|
||
|
* of {@link DnsOptions} and the individual methods of {@link DnsOptions.Builder}.
|
||
|
*
|
||
|
* <p>Only relevant if {@link #enableQuic(boolean)} is enabled.
|
||
|
*
|
||
|
* @return the builder to facilitate chaining.
|
||
|
*/
|
||
|
@DnsOptions.Experimental
|
||
|
public Builder setDnsOptions(DnsOptions dnsOptions) {
|
||
|
mBuilderDelegate.setDnsOptions(dnsOptions);
|
||
|
return this;
|
||
|
}
|
||
|
|
||
|
/** @see #setDnsOptions(DnsOptions) */
|
||
|
@DnsOptions.Experimental
|
||
|
public Builder setDnsOptions(DnsOptions.Builder dnsOptions) {
|
||
|
return setDnsOptions(dnsOptions.build());
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Configures the behavior of connection migration. For more details, see documentation
|
||
|
* of {@link ConnectionMigrationOptions} and the individual methods of {@link
|
||
|
* ConnectionMigrationOptions.Builder}.
|
||
|
*
|
||
|
* <p>Only relevant if {@link #enableQuic(boolean)} is enabled.
|
||
|
*
|
||
|
* @return the builder to facilitate chaining.
|
||
|
*/
|
||
|
@ConnectionMigrationOptions.Experimental
|
||
|
public Builder setConnectionMigrationOptions(
|
||
|
ConnectionMigrationOptions connectionMigrationOptions) {
|
||
|
mBuilderDelegate.setConnectionMigrationOptions(connectionMigrationOptions);
|
||
|
return this;
|
||
|
}
|
||
|
|
||
|
/** @see #setConnectionMigrationOptions(ConnectionMigrationOptions) */
|
||
|
@ConnectionMigrationOptions.Experimental
|
||
|
public Builder setConnectionMigrationOptions(
|
||
|
ConnectionMigrationOptions.Builder connectionMigrationOptionsBuilder) {
|
||
|
return setConnectionMigrationOptions(connectionMigrationOptionsBuilder.build());
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Build a {@link CronetEngine} using this builder's configuration.
|
||
|
*
|
||
|
* @return constructed {@link CronetEngine}.
|
||
|
*/
|
||
|
public CronetEngine build() {
|
||
|
int implLevel = getImplementationApiLevel();
|
||
|
if (implLevel != -1 && implLevel < getMaximumApiLevel()) {
|
||
|
Log.w(
|
||
|
TAG,
|
||
|
"The implementation version is lower than the API version. Calls to "
|
||
|
+ "methods added in API "
|
||
|
+ (implLevel + 1)
|
||
|
+ " and newer will "
|
||
|
+ "likely have no effect.");
|
||
|
}
|
||
|
|
||
|
return mBuilderDelegate.build();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Creates an implementation of {@link ICronetEngineBuilder} that can be used to delegate
|
||
|
* the builder calls to. The method uses {@link CronetProvider} to obtain the list of
|
||
|
* available providers.
|
||
|
*
|
||
|
* @param context Android Context to use.
|
||
|
* @return the created {@code ICronetEngineBuilder}.
|
||
|
*/
|
||
|
private static ICronetEngineBuilder createBuilderDelegate(Context context) {
|
||
|
List<CronetProvider> providers =
|
||
|
new ArrayList<>(CronetProvider.getAllProviders(context));
|
||
|
CronetProvider provider = getEnabledCronetProviders(context, providers).get(0);
|
||
|
if (Log.isLoggable(TAG, Log.DEBUG)) {
|
||
|
Log.d(
|
||
|
TAG,
|
||
|
String.format(
|
||
|
"Using '%s' provider for creating CronetEngine.Builder.",
|
||
|
provider));
|
||
|
}
|
||
|
return provider.createBuilder().mBuilderDelegate;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Returns the list of available and enabled {@link CronetProvider}. The returned list is
|
||
|
* sorted based on the provider versions and types.
|
||
|
*
|
||
|
* @param context Android Context to use.
|
||
|
* @param providers the list of enabled and disabled providers to filter out and sort.
|
||
|
* @return the sorted list of enabled providers. The list contains at least one provider.
|
||
|
* @throws RuntimeException is the list of providers is empty or all of the providers are
|
||
|
* disabled.
|
||
|
*/
|
||
|
@VisibleForTesting
|
||
|
static List<CronetProvider> getEnabledCronetProviders(
|
||
|
Context context, List<CronetProvider> providers) {
|
||
|
// Check that there is at least one available provider.
|
||
|
if (providers.isEmpty()) {
|
||
|
throw new RuntimeException(
|
||
|
"Unable to find any Cronet provider."
|
||
|
+ " Have you included all necessary jars?");
|
||
|
}
|
||
|
|
||
|
// Exclude disabled providers from the list.
|
||
|
for (Iterator<CronetProvider> i = providers.iterator(); i.hasNext(); ) {
|
||
|
CronetProvider provider = i.next();
|
||
|
if (!provider.isEnabled()) {
|
||
|
i.remove();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Check that there is at least one enabled provider.
|
||
|
if (providers.isEmpty()) {
|
||
|
throw new RuntimeException(
|
||
|
"All available Cronet providers are disabled."
|
||
|
+ " A provider should be enabled before it can be used.");
|
||
|
}
|
||
|
|
||
|
// Sort providers based on version and type.
|
||
|
Collections.sort(
|
||
|
providers,
|
||
|
new Comparator<CronetProvider>() {
|
||
|
@Override
|
||
|
public int compare(CronetProvider p1, CronetProvider p2) {
|
||
|
// The fallback provider should always be at the end of the list.
|
||
|
if (CronetProvider.PROVIDER_NAME_FALLBACK.equals(p1.getName())) {
|
||
|
return 1;
|
||
|
}
|
||
|
if (CronetProvider.PROVIDER_NAME_FALLBACK.equals(p2.getName())) {
|
||
|
return -1;
|
||
|
}
|
||
|
// A provider with higher version should go first.
|
||
|
return -compareVersions(p1.getVersion(), p2.getVersion());
|
||
|
}
|
||
|
});
|
||
|
return providers;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Compares two strings that contain versions. The string should only contain dot-separated
|
||
|
* segments that contain an arbitrary number of digits digits [0-9].
|
||
|
*
|
||
|
* @param s1 the first string.
|
||
|
* @param s2 the second string.
|
||
|
* @return -1 if s1<s2, +1 if s1>s2 and 0 if s1=s2. If two versions are equal, the version
|
||
|
* with
|
||
|
* the higher number of segments is considered to be higher.
|
||
|
* @throws IllegalArgumentException if any of the strings contains an illegal version
|
||
|
* number.
|
||
|
*/
|
||
|
@VisibleForTesting
|
||
|
static int compareVersions(String s1, String s2) {
|
||
|
if (s1 == null || s2 == null) {
|
||
|
throw new IllegalArgumentException("The input values cannot be null");
|
||
|
}
|
||
|
String[] s1segments = s1.split("\\.");
|
||
|
String[] s2segments = s2.split("\\.");
|
||
|
for (int i = 0; i < s1segments.length && i < s2segments.length; i++) {
|
||
|
try {
|
||
|
int s1segment = Integer.parseInt(s1segments[i]);
|
||
|
int s2segment = Integer.parseInt(s2segments[i]);
|
||
|
if (s1segment != s2segment) {
|
||
|
return Integer.signum(s1segment - s2segment);
|
||
|
}
|
||
|
} catch (NumberFormatException e) {
|
||
|
throw new IllegalArgumentException(
|
||
|
"Unable to convert version segments into"
|
||
|
+ " integers: "
|
||
|
+ s1segments[i]
|
||
|
+ " & "
|
||
|
+ s2segments[i],
|
||
|
e);
|
||
|
}
|
||
|
}
|
||
|
return Integer.signum(s1segments.length - s2segments.length);
|
||
|
}
|
||
|
|
||
|
private int getMaximumApiLevel() {
|
||
|
return ApiVersion.getMaximumAvailableApiLevel();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Returns the implementation version, the implementation being represented by the delegate
|
||
|
* builder, or {@code -1} if the version couldn't be retrieved.
|
||
|
*/
|
||
|
private int getImplementationApiLevel() {
|
||
|
try {
|
||
|
ClassLoader implClassLoader = mBuilderDelegate.getClass().getClassLoader();
|
||
|
Class<?> implVersionClass =
|
||
|
implClassLoader.loadClass("org.chromium.net.impl.ImplVersion");
|
||
|
Method getApiLevel = implVersionClass.getMethod("getApiLevel");
|
||
|
int implementationApiLevel = (Integer) getApiLevel.invoke(null);
|
||
|
|
||
|
return implementationApiLevel;
|
||
|
} catch (Exception e) {
|
||
|
// Any exception in the block above isn't critical, don't bother the app about it.
|
||
|
return -1;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/** @return a human-readable version string of the engine. */
|
||
|
public abstract String getVersionString();
|
||
|
|
||
|
/**
|
||
|
* Shuts down the {@link CronetEngine} if there are no active requests, otherwise throws an
|
||
|
* exception.
|
||
|
*
|
||
|
* <p>Cannot be called on network thread - the thread Cronet calls into Executor on (which is
|
||
|
* different from the thread the Executor invokes callbacks on). May block until all the {@code
|
||
|
* CronetEngine}'s resources have been cleaned up.
|
||
|
*/
|
||
|
public abstract void shutdown();
|
||
|
|
||
|
/**
|
||
|
* Starts NetLog logging to a file. The NetLog will contain events emitted by all live
|
||
|
* CronetEngines. The NetLog is useful for debugging. The file can be viewed using a Chrome
|
||
|
* browser navigated to chrome://net-internals/#import
|
||
|
*
|
||
|
* @param fileName the complete file path. It must not be empty. If the file exists, it is
|
||
|
* truncated before starting. If actively logging, this method is ignored.
|
||
|
* @param logAll {@code true} to include basic events, user cookies, credentials and all
|
||
|
* transferred bytes in the log. This option presents a privacy risk, since it exposes the
|
||
|
* user's credentials, and should only be used with the user's consent and in situations where
|
||
|
* the log won't be public. {@code false} to just include basic events.
|
||
|
*/
|
||
|
public abstract void startNetLogToFile(String fileName, boolean logAll);
|
||
|
|
||
|
/**
|
||
|
* Stops NetLog logging and flushes file to disk. If a logging session is not in progress, this
|
||
|
* call is ignored.
|
||
|
*/
|
||
|
public abstract void stopNetLog();
|
||
|
|
||
|
/**
|
||
|
* Returns differences in metrics collected by Cronet since the last call to this method.
|
||
|
*
|
||
|
* <p>Cronet collects these metrics globally. This means deltas returned by {@code
|
||
|
* getGlobalMetricsDeltas()} will include measurements of requests processed by other {@link
|
||
|
* CronetEngine} instances. Since this function returns differences in metrics collected since
|
||
|
* the last call, and these metrics are collected globally, a call to any {@code CronetEngine}
|
||
|
* instance's {@code getGlobalMetricsDeltas()} method will affect the deltas returned by any
|
||
|
* other
|
||
|
* {@code CronetEngine} instance's {@code getGlobalMetricsDeltas()}.
|
||
|
*
|
||
|
* <p>Cronet starts collecting these metrics after the first call to {@code
|
||
|
* getGlobalMetricsDeltras()}, so the first call returns no useful data as no metrics have yet
|
||
|
* been collected.
|
||
|
*
|
||
|
* @return differences in metrics collected by Cronet, since the last call to {@code
|
||
|
* getGlobalMetricsDeltas()}, serialized as a <a
|
||
|
* href=https://developers.google.com/protocol-buffers>protobuf
|
||
|
* </a>.
|
||
|
*/
|
||
|
public abstract byte[] getGlobalMetricsDeltas();
|
||
|
|
||
|
/**
|
||
|
* Establishes a new connection to the resource specified by the {@link URL} {@code url}.
|
||
|
*
|
||
|
* <p><b>Note:</b> Cronet's {@link java.net.HttpURLConnection} implementation is subject to
|
||
|
* certain limitations, see {@link #createURLStreamHandlerFactory} for details.
|
||
|
*
|
||
|
* @param url URL of resource to connect to.
|
||
|
* @return an {@link java.net.HttpURLConnection} instance implemented by this CronetEngine.
|
||
|
* @throws IOException if an error occurs while opening the connection.
|
||
|
*/
|
||
|
public abstract URLConnection openConnection(URL url) throws IOException;
|
||
|
|
||
|
/**
|
||
|
* Creates a {@link URLStreamHandlerFactory} to handle HTTP and HTTPS traffic. An instance of
|
||
|
* this class can be installed via {@link URL#setURLStreamHandlerFactory} thus using this
|
||
|
* CronetEngine by default for all requests created via {@link URL#openConnection}.
|
||
|
*
|
||
|
* <p>Cronet does not use certain HTTP features provided via the system:
|
||
|
*
|
||
|
* <ul>
|
||
|
* <li>the HTTP cache installed via {@link HttpResponseCache#install(java.io.File, long)
|
||
|
* HttpResponseCache.install()}
|
||
|
* <li>the HTTP authentication method installed via {@link java.net.Authenticator#setDefault}
|
||
|
* <li>the HTTP cookie storage installed via {@link java.net.CookieHandler#setDefault}
|
||
|
* </ul>
|
||
|
*
|
||
|
* <p>While Cronet supports and encourages requests using the HTTPS protocol, Cronet does not
|
||
|
* provide support for the {@link HttpsURLConnection} API. This lack of support also includes
|
||
|
* not using certain HTTPS features provided via the system:
|
||
|
*
|
||
|
* <ul>
|
||
|
* <li>the HTTPS hostname verifier installed via {@link
|
||
|
* HttpsURLConnection#setDefaultHostnameVerifier(javax.net.ssl.HostnameVerifier)
|
||
|
* HttpsURLConnection.setDefaultHostnameVerifier()}
|
||
|
* <li>the HTTPS socket factory installed via {@link
|
||
|
* HttpsURLConnection#setDefaultSSLSocketFactory(javax.net.ssl.SSLSocketFactory)
|
||
|
* HttpsURLConnection.setDefaultSSLSocketFactory()}
|
||
|
* </ul>
|
||
|
*
|
||
|
* @return an {@link URLStreamHandlerFactory} instance implemented by this CronetEngine.
|
||
|
*/
|
||
|
public abstract URLStreamHandlerFactory createURLStreamHandlerFactory();
|
||
|
|
||
|
/**
|
||
|
* Creates a builder for {@link UrlRequest}. All callbacks for generated {@link UrlRequest}
|
||
|
* objects will be invoked on {@code executor}'s threads. {@code executor} must not run tasks on
|
||
|
* the thread calling {@link Executor#execute} to prevent blocking networking operations and
|
||
|
* causing exceptions during shutdown.
|
||
|
*
|
||
|
* @param url URL for the generated requests.
|
||
|
* @param callback callback object that gets invoked on different events.
|
||
|
* @param executor {@link Executor} on which all callbacks will be invoked.
|
||
|
*/
|
||
|
public abstract UrlRequest.Builder newUrlRequestBuilder(
|
||
|
String url, UrlRequest.Callback callback, Executor executor);
|
||
|
|
||
|
/**
|
||
|
* Creates a builder for {@link BidirectionalStream} objects. All callbacks for generated {@code
|
||
|
* BidirectionalStream} objects will be invoked on {@code executor}. {@code executor} must not
|
||
|
* run tasks on the current thread, otherwise the networking operations may block and exceptions
|
||
|
* may be thrown at shutdown time.
|
||
|
*
|
||
|
* @param url URL for the generated streams.
|
||
|
* @param callback the {@link BidirectionalStream.Callback} object that gets invoked upon
|
||
|
* different events occurring.
|
||
|
* @param executor the {@link Executor} on which {@code callback} methods will be invoked.
|
||
|
* @return the created builder.
|
||
|
*
|
||
|
* {@hide}
|
||
|
*/
|
||
|
public BidirectionalStream.Builder newBidirectionalStreamBuilder(
|
||
|
String url, BidirectionalStream.Callback callback, Executor executor) {
|
||
|
throw new UnsupportedOperationException("Not implemented.");
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Returns the number of active requests.
|
||
|
* <p>
|
||
|
* A request becomes "active" in UrlRequest.start(), assuming that method
|
||
|
* does not throw an exception. It becomes inactive when all callbacks have
|
||
|
* returned and no additional callbacks can be triggered in the future. In
|
||
|
* practice, that means the request is inactive once
|
||
|
* onSucceeded/onCanceled/onFailed has returned and all request finished
|
||
|
* listeners have returned.
|
||
|
*
|
||
|
* <a href="https://developer.android.com/guide/topics/connectivity/cronet/lifecycle">Cronet
|
||
|
* requests's lifecycle</a> for more information.
|
||
|
*/
|
||
|
public int getActiveRequestCount() {
|
||
|
return ACTIVE_REQUEST_COUNT_UNKNOWN;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Registers a listener that gets called after the end of each request with the request info.
|
||
|
*
|
||
|
* <p>The listener is called on an {@link java.util.concurrent.Executor} provided by the
|
||
|
* listener.
|
||
|
*
|
||
|
* @param listener the listener for finished requests.
|
||
|
*/
|
||
|
public void addRequestFinishedListener(RequestFinishedInfo.Listener listener) {}
|
||
|
|
||
|
/**
|
||
|
* Removes a finished request listener.
|
||
|
*
|
||
|
* @param listener the listener to remove.
|
||
|
*/
|
||
|
public void removeRequestFinishedListener(RequestFinishedInfo.Listener listener) {}
|
||
|
|
||
|
/**
|
||
|
* Returns the HTTP RTT estimate (in milliseconds) computed by the network quality estimator.
|
||
|
* Set to {@link #CONNECTION_METRIC_UNKNOWN} if the value is unavailable. This must be called
|
||
|
* after
|
||
|
* {@link Builder#enableNetworkQualityEstimator}, and will throw an exception otherwise.
|
||
|
*
|
||
|
* @return Estimate of the HTTP RTT in milliseconds.
|
||
|
*/
|
||
|
public int getHttpRttMs() {
|
||
|
return CONNECTION_METRIC_UNKNOWN;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Returns the transport RTT estimate (in milliseconds) computed by the network quality
|
||
|
* estimator. Set to {@link #CONNECTION_METRIC_UNKNOWN} if the value is unavailable. This must
|
||
|
* be called after {@link Builder#enableNetworkQualityEstimator}, and will throw an exception
|
||
|
* otherwise.
|
||
|
*
|
||
|
* @return Estimate of the transport RTT in milliseconds.
|
||
|
*/
|
||
|
public int getTransportRttMs() {
|
||
|
return CONNECTION_METRIC_UNKNOWN;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Returns the downstream throughput estimate (in kilobits per second) computed by the network
|
||
|
* quality estimator. Set to {@link #CONNECTION_METRIC_UNKNOWN} if the value is unavailable.
|
||
|
* This must be called after {@link Builder#enableNetworkQualityEstimator}, and will throw an
|
||
|
* exception otherwise.
|
||
|
*
|
||
|
* @return Estimate of the downstream throughput in kilobits per second.
|
||
|
*/
|
||
|
public int getDownstreamThroughputKbps() {
|
||
|
return CONNECTION_METRIC_UNKNOWN;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Starts NetLog logging to a specified directory with a bounded size. The NetLog will contain
|
||
|
* events emitted by all live CronetEngines. The NetLog is useful for debugging. Once logging
|
||
|
* has stopped {@link #stopNetLog}, the data will be written to netlog.json in {@code dirPath}.
|
||
|
* If logging is interrupted, you can stitch the files found in .inprogress subdirectory
|
||
|
* manually using:
|
||
|
* https://chromium.googlesource.com/chromium/src/+/main/net/tools/stitch_net_log_files.py. The
|
||
|
* log can be viewed using a Chrome browser navigated to chrome://net-internals/#import.
|
||
|
*
|
||
|
* @param dirPath the directory where the netlog.json file will be created. dirPath must already
|
||
|
* exist. NetLog files must not exist in the directory. If actively logging, this method is
|
||
|
* ignored.
|
||
|
* @param logAll {@code true} to include basic events, user cookies, credentials and all
|
||
|
* transferred bytes in the log. This option presents a privacy risk, since it exposes the
|
||
|
* user's credentials, and should only be used with the user's consent and in situations where
|
||
|
* the log won't be public. {@code false} to just include basic events.
|
||
|
* @param maxSize the maximum total disk space in bytes that should be used by NetLog. Actual
|
||
|
* disk
|
||
|
* space usage may exceed this limit slightly.
|
||
|
*/
|
||
|
public void startNetLogToDisk(String dirPath, boolean logAll, int maxSize) {}
|
||
|
|
||
|
/**
|
||
|
* Binds the engine to the specified network handle. All requests created through this engine
|
||
|
* will use the network associated to this handle. If this network disconnects all requests will
|
||
|
* fail, the exact error will depend on the stage of request processing when the network
|
||
|
* disconnects. Network handles can be obtained through {@code Network#getNetworkHandle}. Only
|
||
|
* available starting from Android Marshmallow.
|
||
|
*
|
||
|
* @param networkHandle the network handle to bind the engine to. Specify {@link
|
||
|
* #UNBIND_NETWORK_HANDLE} to unbind.
|
||
|
*/
|
||
|
public void bindToNetwork(long networkHandle) {}
|
||
|
|
||
|
/**
|
||
|
* Returns an estimate of the effective connection type computed by the network quality
|
||
|
* estimator. Call {@link Builder#enableNetworkQualityEstimator} to begin computing this value.
|
||
|
*
|
||
|
* @return the estimated connection type. The returned value is one of {@link
|
||
|
* #EFFECTIVE_CONNECTION_TYPE_UNKNOWN EFFECTIVE_CONNECTION_TYPE_* }.
|
||
|
*/
|
||
|
public int getEffectiveConnectionType() {
|
||
|
return EFFECTIVE_CONNECTION_TYPE_UNKNOWN;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Configures the network quality estimator for testing. This must be called before round trip
|
||
|
* time and throughput listeners are added, and after the network quality estimator has been
|
||
|
* enabled.
|
||
|
*
|
||
|
* @param useLocalHostRequests include requests to localhost in estimates.
|
||
|
* @param useSmallerResponses include small responses in throughput estimates.
|
||
|
* @param disableOfflineCheck when set to true, disables the device offline checks when
|
||
|
* computing
|
||
|
* the effective connection type or when writing the prefs.
|
||
|
*/
|
||
|
public void configureNetworkQualityEstimatorForTesting(
|
||
|
boolean useLocalHostRequests,
|
||
|
boolean useSmallerResponses,
|
||
|
boolean disableOfflineCheck) {}
|
||
|
|
||
|
/**
|
||
|
* Registers a listener that gets called whenever the network quality estimator witnesses a
|
||
|
* sample round trip time. This must be called after {@link
|
||
|
* Builder#enableNetworkQualityEstimator}, and with throw an exception otherwise. Round trip
|
||
|
* times may be recorded at various layers of the network stack, including TCP, QUIC, and at the
|
||
|
* URL request layer. The listener is called on the
|
||
|
* {@link java.util.concurrent.Executor} that is passed to {@link
|
||
|
* Builder#enableNetworkQualityEstimator}.
|
||
|
*
|
||
|
* @param listener the listener of round trip times.
|
||
|
*/
|
||
|
public void addRttListener(NetworkQualityRttListener listener) {}
|
||
|
|
||
|
/**
|
||
|
* Removes a listener of round trip times if previously registered with {@link #addRttListener}.
|
||
|
* This should be called after a {@link NetworkQualityRttListener} is added in order to stop
|
||
|
* receiving observations.
|
||
|
*
|
||
|
* @param listener the listener of round trip times.
|
||
|
*/
|
||
|
public void removeRttListener(NetworkQualityRttListener listener) {}
|
||
|
|
||
|
/**
|
||
|
* Registers a listener that gets called whenever the network quality estimator witnesses a
|
||
|
* sample throughput measurement. This must be called after {@link
|
||
|
* Builder#enableNetworkQualityEstimator}. Throughput observations are computed by measuring
|
||
|
* bytes read over the active network interface at times when at least one URL response is being
|
||
|
* received. The listener is called on the {@link java.util.concurrent.Executor} that is passed
|
||
|
* to {@link Builder#enableNetworkQualityEstimator}.
|
||
|
*
|
||
|
* @param listener the listener of throughput.
|
||
|
*/
|
||
|
public void addThroughputListener(NetworkQualityThroughputListener listener) {}
|
||
|
|
||
|
/**
|
||
|
* Removes a listener of throughput. This should be called after a {@link
|
||
|
* NetworkQualityThroughputListener} is added with {@link #addThroughputListener} in order to
|
||
|
* stop receiving observations.
|
||
|
*
|
||
|
* @param listener the listener of throughput.
|
||
|
*/
|
||
|
public void removeThroughputListener(NetworkQualityThroughputListener listener) {}
|
||
|
}
|