1 // Copyright 2014 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 #include "components/cronet/android/cronet_context_adapter.h"
6 #include "components/cronet/android/proto/request_context_config.pb.h"
7
8 #include <limits.h>
9 #include <stddef.h>
10 #include <stdint.h>
11
12 #include <limits>
13 #include <map>
14 #include <set>
15 #include <utility>
16 #include <vector>
17
18 #include "base/android/jni_android.h"
19 #include "base/android/jni_array.h"
20 #include "base/android/jni_string.h"
21 #include "base/base64.h"
22 #include "base/files/file_path.h"
23 #include "base/files/file_util.h"
24 #include "base/files/scoped_file.h"
25 #include "base/functional/bind.h"
26 #include "base/functional/callback.h"
27 #include "base/lazy_instance.h"
28 #include "base/logging.h"
29 #include "base/memory/ptr_util.h"
30 #include "base/metrics/histogram_macros.h"
31 #include "base/task/single_thread_task_runner.h"
32 #include "base/threading/thread_restrictions.h"
33 #include "base/time/time.h"
34 #include "base/values.h"
35 #include "components/cronet/android/cronet_jni_headers/CronetUrlRequestContext_jni.h"
36 #include "components/cronet/android/cronet_library_loader.h"
37 #include "components/cronet/cronet_prefs_manager.h"
38 #include "components/cronet/host_cache_persistence_manager.h"
39 #include "components/cronet/url_request_context_config.h"
40 #include "components/metrics/library_support/histogram_manager.h"
41 #include "net/base/load_flags.h"
42 #include "net/base/logging_network_change_observer.h"
43 #include "net/base/net_errors.h"
44 #include "net/base/network_delegate_impl.h"
45 #include "net/base/url_util.h"
46 #include "net/cert/caching_cert_verifier.h"
47 #include "net/cert/cert_verifier.h"
48 #include "net/cookies/cookie_monster.h"
49 #include "net/http/http_auth_handler_factory.h"
50 #include "net/log/file_net_log_observer.h"
51 #include "net/log/net_log_util.h"
52 #include "net/nqe/network_quality_estimator_params.h"
53 #include "net/proxy_resolution/proxy_config_service_android.h"
54 #include "net/proxy_resolution/proxy_resolution_service.h"
55 #include "net/third_party/quiche/src/quiche/quic/core/quic_versions.h"
56 #include "net/url_request/url_request_context.h"
57 #include "net/url_request/url_request_context_builder.h"
58 #include "net/url_request/url_request_interceptor.h"
59
60 using base::android::JavaParamRef;
61 using base::android::ScopedJavaLocalRef;
62
63 namespace cronet {
64
CronetContextAdapter(std::unique_ptr<URLRequestContextConfig> context_config)65 CronetContextAdapter::CronetContextAdapter(
66 std::unique_ptr<URLRequestContextConfig> context_config) {
67 // Create context and pass ownership of |this| (self) to the context.
68 context_ = new CronetContext(std::move(context_config),
69 base::WrapUnique<CronetContextAdapter>(this));
70 }
71
72 CronetContextAdapter::~CronetContextAdapter() = default;
73
InitRequestContextOnInitThread(JNIEnv * env,const JavaParamRef<jobject> & jcaller)74 void CronetContextAdapter::InitRequestContextOnInitThread(
75 JNIEnv* env,
76 const JavaParamRef<jobject>& jcaller) {
77 jcronet_url_request_context_.Reset(env, jcaller);
78 context_->InitRequestContextOnInitThread();
79 }
80
ConfigureNetworkQualityEstimatorForTesting(JNIEnv * env,const JavaParamRef<jobject> & jcaller,jboolean use_local_host_requests,jboolean use_smaller_responses,jboolean disable_offline_check)81 void CronetContextAdapter::ConfigureNetworkQualityEstimatorForTesting(
82 JNIEnv* env,
83 const JavaParamRef<jobject>& jcaller,
84 jboolean use_local_host_requests,
85 jboolean use_smaller_responses,
86 jboolean disable_offline_check) {
87 context_->ConfigureNetworkQualityEstimatorForTesting(
88 use_local_host_requests == JNI_TRUE, use_smaller_responses == JNI_TRUE,
89 disable_offline_check == JNI_TRUE);
90 }
91
URLRequestContextExistsForTesting(net::handles::NetworkHandle network)92 bool CronetContextAdapter::URLRequestContextExistsForTesting(
93 net::handles::NetworkHandle network) {
94 return context_->URLRequestContextExistsForTesting(network); // IN-TEST
95 }
96
ProvideRTTObservations(JNIEnv * env,const JavaParamRef<jobject> & jcaller,bool should)97 void CronetContextAdapter::ProvideRTTObservations(
98 JNIEnv* env,
99 const JavaParamRef<jobject>& jcaller,
100 bool should) {
101 context_->ProvideRTTObservations(should == JNI_TRUE);
102 }
103
ProvideThroughputObservations(JNIEnv * env,const JavaParamRef<jobject> & jcaller,bool should)104 void CronetContextAdapter::ProvideThroughputObservations(
105 JNIEnv* env,
106 const JavaParamRef<jobject>& jcaller,
107 bool should) {
108 context_->ProvideThroughputObservations(should == JNI_TRUE);
109 }
110
OnInitNetworkThread()111 void CronetContextAdapter::OnInitNetworkThread() {
112 JNIEnv* env = base::android::AttachCurrentThread();
113 Java_CronetUrlRequestContext_initNetworkThread(env,
114 jcronet_url_request_context_);
115 }
116
OnDestroyNetworkThread()117 void CronetContextAdapter::OnDestroyNetworkThread() {
118 // The |context_| is destroyed.
119 context_ = nullptr;
120 }
121
OnEffectiveConnectionTypeChanged(net::EffectiveConnectionType effective_connection_type)122 void CronetContextAdapter::OnEffectiveConnectionTypeChanged(
123 net::EffectiveConnectionType effective_connection_type) {
124 Java_CronetUrlRequestContext_onEffectiveConnectionTypeChanged(
125 base::android::AttachCurrentThread(), jcronet_url_request_context_,
126 effective_connection_type);
127 }
128
OnRTTOrThroughputEstimatesComputed(int32_t http_rtt_ms,int32_t transport_rtt_ms,int32_t downstream_throughput_kbps)129 void CronetContextAdapter::OnRTTOrThroughputEstimatesComputed(
130 int32_t http_rtt_ms,
131 int32_t transport_rtt_ms,
132 int32_t downstream_throughput_kbps) {
133 Java_CronetUrlRequestContext_onRTTOrThroughputEstimatesComputed(
134 base::android::AttachCurrentThread(), jcronet_url_request_context_,
135 http_rtt_ms, transport_rtt_ms, downstream_throughput_kbps);
136 }
137
OnRTTObservation(int32_t rtt_ms,int32_t timestamp_ms,net::NetworkQualityObservationSource source)138 void CronetContextAdapter::OnRTTObservation(
139 int32_t rtt_ms,
140 int32_t timestamp_ms,
141 net::NetworkQualityObservationSource source) {
142 Java_CronetUrlRequestContext_onRttObservation(
143 base::android::AttachCurrentThread(), jcronet_url_request_context_,
144 rtt_ms, timestamp_ms, source);
145 }
146
OnThroughputObservation(int32_t throughput_kbps,int32_t timestamp_ms,net::NetworkQualityObservationSource source)147 void CronetContextAdapter::OnThroughputObservation(
148 int32_t throughput_kbps,
149 int32_t timestamp_ms,
150 net::NetworkQualityObservationSource source) {
151 Java_CronetUrlRequestContext_onThroughputObservation(
152 base::android::AttachCurrentThread(), jcronet_url_request_context_,
153 throughput_kbps, timestamp_ms, source);
154 }
155
OnStopNetLogCompleted()156 void CronetContextAdapter::OnStopNetLogCompleted() {
157 Java_CronetUrlRequestContext_stopNetLogCompleted(
158 base::android::AttachCurrentThread(), jcronet_url_request_context_);
159 }
160
Destroy(JNIEnv * env,const JavaParamRef<jobject> & jcaller)161 void CronetContextAdapter::Destroy(JNIEnv* env,
162 const JavaParamRef<jobject>& jcaller) {
163 // Deleting |context_| on client thread will post cleanup onto network thread,
164 // which will in turn delete |this| on network thread.
165 delete context_;
166 }
167
GetURLRequestContext(net::handles::NetworkHandle network)168 net::URLRequestContext* CronetContextAdapter::GetURLRequestContext(
169 net::handles::NetworkHandle network) {
170 return context_->GetURLRequestContext(network);
171 }
172
PostTaskToNetworkThread(const base::Location & posted_from,base::OnceClosure callback)173 void CronetContextAdapter::PostTaskToNetworkThread(
174 const base::Location& posted_from,
175 base::OnceClosure callback) {
176 context_->PostTaskToNetworkThread(posted_from, std::move(callback));
177 }
178
IsOnNetworkThread() const179 bool CronetContextAdapter::IsOnNetworkThread() const {
180 return context_->IsOnNetworkThread();
181 }
182
StartNetLogToFile(JNIEnv * env,const JavaParamRef<jobject> & jcaller,const JavaParamRef<jstring> & jfile_name,jboolean jlog_all)183 bool CronetContextAdapter::StartNetLogToFile(
184 JNIEnv* env,
185 const JavaParamRef<jobject>& jcaller,
186 const JavaParamRef<jstring>& jfile_name,
187 jboolean jlog_all) {
188 std::string file_name(
189 base::android::ConvertJavaStringToUTF8(env, jfile_name));
190 return context_->StartNetLogToFile(file_name, jlog_all == JNI_TRUE);
191 }
192
StartNetLogToDisk(JNIEnv * env,const JavaParamRef<jobject> & jcaller,const JavaParamRef<jstring> & jdir_name,jboolean jlog_all,jint jmax_size)193 void CronetContextAdapter::StartNetLogToDisk(
194 JNIEnv* env,
195 const JavaParamRef<jobject>& jcaller,
196 const JavaParamRef<jstring>& jdir_name,
197 jboolean jlog_all,
198 jint jmax_size) {
199 std::string dir_name(base::android::ConvertJavaStringToUTF8(env, jdir_name));
200 context_->StartNetLogToDisk(dir_name, jlog_all == JNI_TRUE, jmax_size);
201 }
202
StopNetLog(JNIEnv * env,const JavaParamRef<jobject> & jcaller)203 void CronetContextAdapter::StopNetLog(JNIEnv* env,
204 const JavaParamRef<jobject>& jcaller) {
205 context_->StopNetLog();
206 }
207
FlushWritePropertiesForTesting(JNIEnv * env,const base::android::JavaParamRef<jobject> & jcaller)208 void CronetContextAdapter::FlushWritePropertiesForTesting(
209 JNIEnv* env,
210 const base::android::JavaParamRef<jobject>& jcaller) {
211 context_->FlushWritePropertiesForTesting(); // IN-TEST
212 }
213
default_load_flags() const214 int CronetContextAdapter::default_load_flags() const {
215 return context_->default_load_flags();
216 }
217
218 // Create a URLRequestContextConfig from the given parameters.
JNI_CronetUrlRequestContext_CreateRequestContextConfig(JNIEnv * env,const JavaParamRef<jbyteArray> & javaSerializedProto)219 static jlong JNI_CronetUrlRequestContext_CreateRequestContextConfig(
220 JNIEnv* env,
221 const JavaParamRef<jbyteArray>& javaSerializedProto) {
222 const int serializedProtoLength = env->GetArrayLength(javaSerializedProto);
223 org::chromium::net::RequestContextConfigOptions configOptions;
224
225 std::vector<uint8_t> serializedProto;
226
227 base::android::JavaByteArrayToByteVector(env, javaSerializedProto,
228 &serializedProto);
229
230 if (!configOptions.ParseFromArray(serializedProto.data(),
231 serializedProtoLength)) {
232 return 0;
233 }
234
235 std::unique_ptr<URLRequestContextConfig> url_request_context_config =
236 URLRequestContextConfig::CreateURLRequestContextConfig(
237 configOptions.quic_enabled(), configOptions.http2_enabled(),
238 configOptions.brotli_enabled(),
239 static_cast<URLRequestContextConfig::HttpCacheType>(
240 configOptions.http_cache_mode()),
241 configOptions.http_cache_max_size(), configOptions.disable_cache(),
242 configOptions.storage_path(),
243 /* accept_languages */ std::string(), configOptions.user_agent(),
244 configOptions.experimental_options(),
245 base::WrapUnique(reinterpret_cast<net::CertVerifier*>(
246 configOptions.mock_cert_verifier())),
247 configOptions.enable_network_quality_estimator(),
248 configOptions.bypass_public_key_pinning_for_local_trust_anchors(),
249 configOptions.network_thread_priority() >= -20 &&
250 configOptions.network_thread_priority() <= 19
251 ? std::optional<double>(configOptions.network_thread_priority())
252 : std::optional<double>());
253 return reinterpret_cast<jlong>(url_request_context_config.release());
254 }
255
256 // Add a QUIC hint to a URLRequestContextConfig.
JNI_CronetUrlRequestContext_AddQuicHint(JNIEnv * env,jlong jurl_request_context_config,const JavaParamRef<jstring> & jhost,jint jport,jint jalternate_port)257 static void JNI_CronetUrlRequestContext_AddQuicHint(
258 JNIEnv* env,
259 jlong jurl_request_context_config,
260 const JavaParamRef<jstring>& jhost,
261 jint jport,
262 jint jalternate_port) {
263 URLRequestContextConfig* config =
264 reinterpret_cast<URLRequestContextConfig*>(jurl_request_context_config);
265 config->quic_hints.push_back(
266 std::make_unique<URLRequestContextConfig::QuicHint>(
267 base::android::ConvertJavaStringToUTF8(env, jhost), jport,
268 jalternate_port));
269 }
270
271 // Add a public key pin to URLRequestContextConfig.
272 // |jhost| is the host to apply the pin to.
273 // |jhashes| is an array of jbyte[32] representing SHA256 key hashes.
274 // |jinclude_subdomains| indicates if pin should be applied to subdomains.
275 // |jexpiration_time| is the time that the pin expires, in milliseconds since
276 // Jan. 1, 1970, midnight GMT.
JNI_CronetUrlRequestContext_AddPkp(JNIEnv * env,jlong jurl_request_context_config,const JavaParamRef<jstring> & jhost,const JavaParamRef<jobjectArray> & jhashes,jboolean jinclude_subdomains,jlong jexpiration_time)277 static void JNI_CronetUrlRequestContext_AddPkp(
278 JNIEnv* env,
279 jlong jurl_request_context_config,
280 const JavaParamRef<jstring>& jhost,
281 const JavaParamRef<jobjectArray>& jhashes,
282 jboolean jinclude_subdomains,
283 jlong jexpiration_time) {
284 URLRequestContextConfig* config =
285 reinterpret_cast<URLRequestContextConfig*>(jurl_request_context_config);
286 std::unique_ptr<URLRequestContextConfig::Pkp> pkp(
287 new URLRequestContextConfig::Pkp(
288 base::android::ConvertJavaStringToUTF8(env, jhost),
289 jinclude_subdomains,
290 base::Time::UnixEpoch() + base::Milliseconds(jexpiration_time)));
291 for (auto bytes_array : jhashes.ReadElements<jbyteArray>()) {
292 static_assert(std::is_pod<net::SHA256HashValue>::value,
293 "net::SHA256HashValue is not POD");
294 static_assert(sizeof(net::SHA256HashValue) * CHAR_BIT == 256,
295 "net::SHA256HashValue contains overhead");
296 if (env->GetArrayLength(bytes_array.obj()) !=
297 sizeof(net::SHA256HashValue)) {
298 LOG(ERROR) << "Unable to add public key hash value.";
299 continue;
300 }
301 jbyte* bytes = env->GetByteArrayElements(bytes_array.obj(), nullptr);
302 net::HashValue hash(*reinterpret_cast<net::SHA256HashValue*>(bytes));
303 pkp->pin_hashes.push_back(hash);
304 env->ReleaseByteArrayElements(bytes_array.obj(), bytes, JNI_ABORT);
305 }
306 config->pkp_list.push_back(std::move(pkp));
307 }
308
309 // Creates RequestContextAdater if config is valid URLRequestContextConfig,
310 // returns 0 otherwise.
JNI_CronetUrlRequestContext_CreateRequestContextAdapter(JNIEnv * env,jlong jconfig)311 static jlong JNI_CronetUrlRequestContext_CreateRequestContextAdapter(
312 JNIEnv* env,
313 jlong jconfig) {
314 std::unique_ptr<URLRequestContextConfig> context_config(
315 reinterpret_cast<URLRequestContextConfig*>(jconfig));
316
317 CronetContextAdapter* context_adapter =
318 new CronetContextAdapter(std::move(context_config));
319 return reinterpret_cast<jlong>(context_adapter);
320 }
321
322 static ScopedJavaLocalRef<jbyteArray>
JNI_CronetUrlRequestContext_GetHistogramDeltas(JNIEnv * env)323 JNI_CronetUrlRequestContext_GetHistogramDeltas(JNIEnv* env) {
324 std::vector<uint8_t> data;
325 if (!metrics::HistogramManager::GetInstance()->GetDeltas(&data))
326 return ScopedJavaLocalRef<jbyteArray>();
327 return base::android::ToJavaByteArray(env, data);
328 }
329
330 } // namespace cronet
331