1 // Copyright 2015 The Chromium Authors 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 package org.chromium.net.impl; 6 7 import static android.os.Process.THREAD_PRIORITY_BACKGROUND; 8 import static android.os.Process.THREAD_PRIORITY_MORE_FAVORABLE; 9 10 import android.content.Context; 11 import android.os.Build; 12 import android.util.Log; 13 14 import org.chromium.net.BidirectionalStream; 15 import org.chromium.net.ExperimentalBidirectionalStream; 16 import org.chromium.net.NetworkQualityRttListener; 17 import org.chromium.net.NetworkQualityThroughputListener; 18 import org.chromium.net.RequestFinishedInfo; 19 import org.chromium.net.UrlRequest; 20 import org.chromium.net.impl.CronetLogger.CronetSource; 21 import org.chromium.net.impl.CronetLogger.CronetVersion; 22 23 import java.io.IOException; 24 import java.net.Proxy; 25 import java.net.URL; 26 import java.net.URLConnection; 27 import java.net.URLStreamHandler; 28 import java.net.URLStreamHandlerFactory; 29 import java.util.Collection; 30 import java.util.List; 31 import java.util.Map; 32 import java.util.concurrent.Executor; 33 import java.util.concurrent.ExecutorService; 34 import java.util.concurrent.Executors; 35 import java.util.concurrent.LinkedBlockingQueue; 36 import java.util.concurrent.ThreadFactory; 37 import java.util.concurrent.ThreadPoolExecutor; 38 import java.util.concurrent.TimeUnit; 39 import java.util.concurrent.atomic.AtomicInteger; 40 41 /** 42 * {@link java.net.HttpURLConnection} backed CronetEngine. 43 * 44 * <p>Does not support netlogs, transferred data measurement, bidistream, cache, or priority. 45 */ 46 public final class JavaCronetEngine extends CronetEngineBase { 47 private static final String TAG = JavaCronetEngine.class.getSimpleName(); 48 49 private final String mUserAgent; 50 private final ExecutorService mExecutorService; 51 private final int mCronetEngineId; 52 private final CronetLogger mLogger; 53 private final AtomicInteger mActiveRequestCount = new AtomicInteger(); 54 55 /** The network handle to be used for requests that do not explicitly specify one. **/ 56 private long mNetworkHandle = DEFAULT_NETWORK_HANDLE; 57 58 private final Context mContext; 59 JavaCronetEngine(CronetEngineBuilderImpl builder)60 public JavaCronetEngine(CronetEngineBuilderImpl builder) { 61 mContext = builder.getContext(); 62 mCronetEngineId = hashCode(); 63 // On android, all background threads (and all threads that are part 64 // of background processes) are put in a cgroup that is allowed to 65 // consume up to 5% of CPU - these worker threads spend the vast 66 // majority of their time waiting on I/O, so making them contend with 67 // background applications for a slice of CPU doesn't make much sense. 68 // We want to hurry up and get idle. 69 final int threadPriority = 70 builder.threadPriority(THREAD_PRIORITY_BACKGROUND + THREAD_PRIORITY_MORE_FAVORABLE); 71 this.mUserAgent = builder.getUserAgent(); 72 // For unbounded work queues, the effective maximum pool size is 73 // equivalent to the core pool size. 74 this.mExecutorService = 75 new ThreadPoolExecutor( 76 10, 77 10, 78 50, 79 TimeUnit.SECONDS, 80 new LinkedBlockingQueue<Runnable>(), 81 new ThreadFactory() { 82 @Override 83 public Thread newThread(final Runnable r) { 84 return Executors.defaultThreadFactory() 85 .newThread( 86 new Runnable() { 87 @Override 88 public void run() { 89 Thread.currentThread() 90 .setName("JavaCronetEngine"); 91 android.os.Process.setThreadPriority( 92 threadPriority); 93 r.run(); 94 } 95 }); 96 } 97 }); 98 mLogger = CronetLoggerFactory.createLogger(mContext, CronetSource.CRONET_SOURCE_FALLBACK); 99 try { 100 mLogger.logCronetEngineCreation( 101 mCronetEngineId, 102 builder.toLoggerInfo(), 103 buildCronetVersion(), 104 CronetSource.CRONET_SOURCE_FALLBACK); 105 } catch (RuntimeException e) { 106 // Handle any issue gracefully, we should never crash due failures while logging. 107 Log.e(TAG, "Error while trying to log JavaCronetEngine creation: ", e); 108 } 109 Log.w( 110 TAG, 111 "using the fallback Cronet Engine implementation. Performance will suffer " 112 + "and many HTTP client features, including caching, will not work."); 113 } 114 115 /** Increment the number of active requests. */ incrementActiveRequestCount()116 void incrementActiveRequestCount() { 117 mActiveRequestCount.incrementAndGet(); 118 } 119 120 /** Decrement the number of active requests. */ decrementActiveRequestCount()121 void decrementActiveRequestCount() { 122 mActiveRequestCount.decrementAndGet(); 123 } 124 getCronetEngineId()125 int getCronetEngineId() { 126 return mCronetEngineId; 127 } 128 getCronetLogger()129 CronetLogger getCronetLogger() { 130 return mLogger; 131 } 132 getContext()133 Context getContext() { 134 return mContext; 135 } 136 137 @Override 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)138 public UrlRequestBase createRequest( 139 String url, 140 UrlRequest.Callback callback, 141 Executor executor, 142 int priority, 143 Collection<Object> connectionAnnotations, 144 boolean disableCache, 145 boolean disableConnectionMigration, 146 boolean allowDirectExecutor, 147 boolean trafficStatsTagSet, 148 int trafficStatsTag, 149 boolean trafficStatsUidSet, 150 int trafficStatsUid, 151 RequestFinishedInfo.Listener requestFinishedListener, 152 int idempotency, 153 long networkHandle) { 154 if (networkHandle != DEFAULT_NETWORK_HANDLE) { 155 mNetworkHandle = networkHandle; 156 } 157 return new JavaUrlRequest( 158 this, 159 callback, 160 mExecutorService, 161 executor, 162 url, 163 mUserAgent, 164 allowDirectExecutor, 165 trafficStatsTagSet, 166 trafficStatsTag, 167 trafficStatsUidSet, 168 trafficStatsUid, 169 mNetworkHandle); 170 } 171 172 @Override 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)173 protected ExperimentalBidirectionalStream createBidirectionalStream( 174 String url, 175 BidirectionalStream.Callback callback, 176 Executor executor, 177 String httpMethod, 178 List<Map.Entry<String, String>> requestHeaders, 179 @StreamPriority int priority, 180 boolean delayRequestHeadersUntilFirstFlush, 181 Collection<Object> connectionAnnotations, 182 boolean trafficStatsTagSet, 183 int trafficStatsTag, 184 boolean trafficStatsUidSet, 185 int trafficStatsUid, 186 long networkHandle) { 187 throw new UnsupportedOperationException( 188 "Can't create a bidi stream - httpurlconnection doesn't have those APIs"); 189 } 190 191 @Override newBidirectionalStreamBuilder( String url, BidirectionalStream.Callback callback, Executor executor)192 public ExperimentalBidirectionalStream.Builder newBidirectionalStreamBuilder( 193 String url, BidirectionalStream.Callback callback, Executor executor) { 194 throw new UnsupportedOperationException( 195 "The bidirectional stream API is not supported by the Java implementation " 196 + "of Cronet Engine"); 197 } 198 199 @Override getVersionString()200 public String getVersionString() { 201 return "CronetHttpURLConnection/" + ImplVersion.getCronetVersionWithLastChange(); 202 } 203 buildCronetVersion()204 private CronetVersion buildCronetVersion() { 205 String version = getVersionString(); 206 // getVersionString()'s output looks like "Cronet/w.x.y.z@hash". CronetVersion only cares 207 // about the "w.x.y.z" bit. 208 version = version.split("/")[1]; 209 version = version.split("@")[0]; 210 return new CronetVersion(version); 211 } 212 213 @Override shutdown()214 public void shutdown() { 215 mExecutorService.shutdown(); 216 } 217 218 @Override startNetLogToFile(String fileName, boolean logAll)219 public void startNetLogToFile(String fileName, boolean logAll) {} 220 221 @Override startNetLogToDisk(String dirPath, boolean logAll, int maxSize)222 public void startNetLogToDisk(String dirPath, boolean logAll, int maxSize) {} 223 224 @Override stopNetLog()225 public void stopNetLog() {} 226 227 @Override getGlobalMetricsDeltas()228 public byte[] getGlobalMetricsDeltas() { 229 return new byte[0]; 230 } 231 232 @Override getEffectiveConnectionType()233 public int getEffectiveConnectionType() { 234 return EFFECTIVE_CONNECTION_TYPE_UNKNOWN; 235 } 236 237 @Override getHttpRttMs()238 public int getHttpRttMs() { 239 return CONNECTION_METRIC_UNKNOWN; 240 } 241 242 @Override getTransportRttMs()243 public int getTransportRttMs() { 244 return CONNECTION_METRIC_UNKNOWN; 245 } 246 247 @Override getDownstreamThroughputKbps()248 public int getDownstreamThroughputKbps() { 249 return CONNECTION_METRIC_UNKNOWN; 250 } 251 252 @Override getActiveRequestCount()253 public int getActiveRequestCount() { 254 return mActiveRequestCount.get(); 255 } 256 257 @Override bindToNetwork(long networkHandle)258 public void bindToNetwork(long networkHandle) { 259 if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { 260 throw new UnsupportedOperationException( 261 "This multi-network Java implementation is available starting from Android" 262 + " Pie"); 263 } 264 mNetworkHandle = networkHandle; 265 } 266 267 @Override configureNetworkQualityEstimatorForTesting( boolean useLocalHostRequests, boolean useSmallerResponses, boolean disableOfflineCheck)268 public void configureNetworkQualityEstimatorForTesting( 269 boolean useLocalHostRequests, 270 boolean useSmallerResponses, 271 boolean disableOfflineCheck) {} 272 273 @Override addRttListener(NetworkQualityRttListener listener)274 public void addRttListener(NetworkQualityRttListener listener) {} 275 276 @Override removeRttListener(NetworkQualityRttListener listener)277 public void removeRttListener(NetworkQualityRttListener listener) {} 278 279 @Override addThroughputListener(NetworkQualityThroughputListener listener)280 public void addThroughputListener(NetworkQualityThroughputListener listener) {} 281 282 @Override removeThroughputListener(NetworkQualityThroughputListener listener)283 public void removeThroughputListener(NetworkQualityThroughputListener listener) {} 284 285 @Override addRequestFinishedListener(RequestFinishedInfo.Listener listener)286 public void addRequestFinishedListener(RequestFinishedInfo.Listener listener) {} 287 288 @Override removeRequestFinishedListener(RequestFinishedInfo.Listener listener)289 public void removeRequestFinishedListener(RequestFinishedInfo.Listener listener) {} 290 291 @Override openConnection(URL url)292 public URLConnection openConnection(URL url) throws IOException { 293 return url.openConnection(); 294 } 295 296 @Override openConnection(URL url, Proxy proxy)297 public URLConnection openConnection(URL url, Proxy proxy) throws IOException { 298 return url.openConnection(proxy); 299 } 300 301 @Override createURLStreamHandlerFactory()302 public URLStreamHandlerFactory createURLStreamHandlerFactory() { 303 // Returning null causes this factory to pass though, which ends up using the platform's 304 // implementation. 305 return new URLStreamHandlerFactory() { 306 @Override 307 public URLStreamHandler createURLStreamHandler(String protocol) { 308 return null; 309 } 310 }; 311 } 312 } 313