blob: dfc59c7a0de7dd178ebc6c3a1d0b8553ee4b8a25 [file] [log] [blame]
// 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.impl;
import static android.os.Process.THREAD_PRIORITY_BACKGROUND;
import static android.os.Process.THREAD_PRIORITY_MORE_FAVORABLE;
import android.content.Context;
import android.os.Build;
import android.util.Log;
import org.chromium.net.BidirectionalStream;
import org.chromium.net.ExperimentalBidirectionalStream;
import org.chromium.net.NetworkQualityRttListener;
import org.chromium.net.NetworkQualityThroughputListener;
import org.chromium.net.RequestFinishedInfo;
import org.chromium.net.UrlRequest;
import org.chromium.net.impl.CronetLogger.CronetEngineBuilderInfo;
import org.chromium.net.impl.CronetLogger.CronetSource;
import org.chromium.net.impl.CronetLogger.CronetVersion;
import java.io.IOException;
import java.net.Proxy;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLStreamHandler;
import java.net.URLStreamHandlerFactory;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
/**
* {@link java.net.HttpURLConnection} backed CronetEngine.
*
* <p>Does not support netlogs, transferred data measurement, bidistream, cache, or priority.
*/
public final class JavaCronetEngine extends CronetEngineBase {
private static final String TAG = JavaCronetEngine.class.getSimpleName();
private final String mUserAgent;
private final ExecutorService mExecutorService;
private final int mCronetEngineId;
private final CronetLogger mLogger;
private final AtomicInteger mActiveRequestCount = new AtomicInteger();
/** The network handle to be used for requests that do not explicitly specify one. **/
private long mNetworkHandle = DEFAULT_NETWORK_HANDLE;
private final Context mContext;
public JavaCronetEngine(CronetEngineBuilderImpl builder) {
mContext = builder.getContext();
mCronetEngineId = hashCode();
// On android, all background threads (and all threads that are part
// of background processes) are put in a cgroup that is allowed to
// consume up to 5% of CPU - these worker threads spend the vast
// majority of their time waiting on I/O, so making them contend with
// background applications for a slice of CPU doesn't make much sense.
// We want to hurry up and get idle.
final int threadPriority =
builder.threadPriority(THREAD_PRIORITY_BACKGROUND + THREAD_PRIORITY_MORE_FAVORABLE);
this.mUserAgent = builder.getUserAgent();
// For unbounded work queues, the effective maximum pool size is
// equivalent to the core pool size.
this.mExecutorService =
new ThreadPoolExecutor(
10,
10,
50,
TimeUnit.SECONDS,
new LinkedBlockingQueue<Runnable>(),
new ThreadFactory() {
@Override
public Thread newThread(final Runnable r) {
return Executors.defaultThreadFactory()
.newThread(
new Runnable() {
@Override
public void run() {
Thread.currentThread()
.setName("JavaCronetEngine");
android.os.Process.setThreadPriority(
threadPriority);
r.run();
}
});
}
});
mLogger = CronetLoggerFactory.createNoOpLogger();
try {
mLogger.logCronetEngineCreation(
mCronetEngineId,
new CronetEngineBuilderInfo(builder),
buildCronetVersion(),
CronetSource.CRONET_SOURCE_FALLBACK);
} catch (RuntimeException e) {
// Handle any issue gracefully, we should never crash due failures while logging.
Log.e(TAG, "Error while trying to log JavaCronetEngine creation: ", e);
}
Log.w(
TAG,
"using the fallback Cronet Engine implementation. Performance will suffer "
+ "and many HTTP client features, including caching, will not work.");
}
/** Increment the number of active requests. */
void incrementActiveRequestCount() {
mActiveRequestCount.incrementAndGet();
}
/** Decrement the number of active requests. */
void decrementActiveRequestCount() {
mActiveRequestCount.decrementAndGet();
}
int getCronetEngineId() {
return mCronetEngineId;
}
CronetLogger getCronetLogger() {
return mLogger;
}
Context getContext() {
return mContext;
}
@Override
public UrlRequestBase createRequest(
String url,
UrlRequest.Callback callback,
Executor executor,
int priority,
Collection<Object> connectionAnnotations,
boolean disableCache,
boolean disableConnectionMigration,
boolean allowDirectExecutor,
boolean trafficStatsTagSet,
int trafficStatsTag,
boolean trafficStatsUidSet,
int trafficStatsUid,
RequestFinishedInfo.Listener requestFinishedListener,
int idempotency,
long networkHandle) {
if (networkHandle != DEFAULT_NETWORK_HANDLE) {
mNetworkHandle = networkHandle;
}
return new JavaUrlRequest(
this,
callback,
mExecutorService,
executor,
url,
mUserAgent,
allowDirectExecutor,
trafficStatsTagSet,
trafficStatsTag,
trafficStatsUidSet,
trafficStatsUid,
mNetworkHandle);
}
@Override
protected ExperimentalBidirectionalStream createBidirectionalStream(
String url,
BidirectionalStream.Callback callback,
Executor executor,
String httpMethod,
List<Map.Entry<String, String>> requestHeaders,
@StreamPriority int priority,
boolean delayRequestHeadersUntilFirstFlush,
Collection<Object> connectionAnnotations,
boolean trafficStatsTagSet,
int trafficStatsTag,
boolean trafficStatsUidSet,
int trafficStatsUid,
long networkHandle) {
throw new UnsupportedOperationException(
"Can't create a bidi stream - httpurlconnection doesn't have those APIs");
}
@Override
public ExperimentalBidirectionalStream.Builder newBidirectionalStreamBuilder(
String url, BidirectionalStream.Callback callback, Executor executor) {
throw new UnsupportedOperationException(
"The bidirectional stream API is not supported by the Java implementation "
+ "of Cronet Engine");
}
@Override
public String getVersionString() {
return "CronetHttpURLConnection/" + ImplVersion.getCronetVersionWithLastChange();
}
private CronetVersion buildCronetVersion() {
String version = getVersionString();
// getVersionString()'s output looks like "Cronet/w.x.y.z@hash". CronetVersion only cares
// about the "w.x.y.z" bit.
version = version.split("/")[1];
version = version.split("@")[0];
return new CronetVersion(version);
}
@Override
public void shutdown() {
mExecutorService.shutdown();
}
@Override
public void startNetLogToFile(String fileName, boolean logAll) {}
@Override
public void startNetLogToDisk(String dirPath, boolean logAll, int maxSize) {}
@Override
public void stopNetLog() {}
@Override
public byte[] getGlobalMetricsDeltas() {
return new byte[0];
}
@Override
public int getEffectiveConnectionType() {
return EFFECTIVE_CONNECTION_TYPE_UNKNOWN;
}
@Override
public int getHttpRttMs() {
return CONNECTION_METRIC_UNKNOWN;
}
@Override
public int getTransportRttMs() {
return CONNECTION_METRIC_UNKNOWN;
}
@Override
public int getDownstreamThroughputKbps() {
return CONNECTION_METRIC_UNKNOWN;
}
@Override
public int getActiveRequestCount() {
return mActiveRequestCount.get();
}
@Override
public void bindToNetwork(long networkHandle) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
throw new UnsupportedOperationException(
"This multi-network Java implementation is available starting from Android Pie");
}
mNetworkHandle = networkHandle;
}
@Override
public void configureNetworkQualityEstimatorForTesting(
boolean useLocalHostRequests,
boolean useSmallerResponses,
boolean disableOfflineCheck) {}
@Override
public void addRttListener(NetworkQualityRttListener listener) {}
@Override
public void removeRttListener(NetworkQualityRttListener listener) {}
@Override
public void addThroughputListener(NetworkQualityThroughputListener listener) {}
@Override
public void removeThroughputListener(NetworkQualityThroughputListener listener) {}
@Override
public void addRequestFinishedListener(RequestFinishedInfo.Listener listener) {}
@Override
public void removeRequestFinishedListener(RequestFinishedInfo.Listener listener) {}
@Override
public URLConnection openConnection(URL url) throws IOException {
return url.openConnection();
}
@Override
public URLConnection openConnection(URL url, Proxy proxy) throws IOException {
return url.openConnection(proxy);
}
@Override
public URLStreamHandlerFactory createURLStreamHandlerFactory() {
// Returning null causes this factory to pass though, which ends up using the platform's
// implementation.
return new URLStreamHandlerFactory() {
@Override
public URLStreamHandler createURLStreamHandler(String protocol) {
return null;
}
};
}
}