| // 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; |
| } |
| }; |
| } |
| } |