// 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. *

{@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. * *

NOTE: 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 QUIC 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 HTTP/2 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 Brotli 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. * *

Calling this method multiple times with the same host name overrides the previously * set pins for the host. * *

More information about the public key pinning can be found in RFC 7469. * * @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 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}. * *

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}. * *

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}. * *

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 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 getEnabledCronetProviders( Context context, List 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 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() { @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 s1s2 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. * *

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. * *

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()}. * *

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 protobuf * . */ public abstract byte[] getGlobalMetricsDeltas(); /** * Establishes a new connection to the resource specified by the {@link URL} {@code url}. * *

Note: 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}. * *

Cronet does not use certain HTTP features provided via the system: * *

* *

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: * *

* * @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. *

* 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. * * Cronet * requests's lifecycle 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. * *

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) {} }