blob: b44eedc6a0ed8c01d4c2a69d6544abc61a581622 [file] [log] [blame]
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/cronet/android/cronet_url_request_context_adapter.h"
#include <limits.h>
#include <stddef.h>
#include <stdint.h>
#include <limits>
#include <map>
#include <set>
#include <utility>
#include <vector>
#include "base/android/jni_android.h"
#include "base/android/jni_array.h"
#include "base/android/jni_string.h"
#include "base/base64.h"
#include "base/bind.h"
#include "base/callback.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/files/scoped_file.h"
#include "base/lazy_instance.h"
#include "base/logging.h"
#include "base/macros.h"
#include "base/metrics/histogram_macros.h"
#include "base/single_thread_task_runner.h"
#include "base/threading/thread_restrictions.h"
#include "base/threading/thread_task_runner_handle.h"
#include "base/time/time.h"
#include "base/values.h"
#include "components/cronet/android/buildflags.h"
#include "components/cronet/android/cronet_jni_headers/CronetUrlRequestContext_jni.h"
#include "components/cronet/android/cronet_library_loader.h"
#include "components/cronet/cronet_prefs_manager.h"
#include "components/cronet/histogram_manager.h"
#include "components/cronet/host_cache_persistence_manager.h"
#include "components/cronet/url_request_context_config.h"
#include "net/base/load_flags.h"
#include "net/base/logging_network_change_observer.h"
#include "net/base/net_errors.h"
#include "net/base/network_delegate_impl.h"
#include "net/base/url_util.h"
#include "net/cert/caching_cert_verifier.h"
#include "net/cert/cert_verifier.h"
#include "net/cookies/cookie_monster.h"
#include "net/http/http_auth_handler_factory.h"
#include "net/log/file_net_log_observer.h"
#include "net/log/net_log_util.h"
#include "net/nqe/network_quality_estimator_params.h"
#include "net/proxy_resolution/proxy_config_service_android.h"
#include "net/proxy_resolution/proxy_resolution_service.h"
#include "net/third_party/quiche/src/quic/core/quic_versions.h"
#include "net/url_request/url_request_context.h"
#include "net/url_request/url_request_context_builder.h"
#include "net/url_request/url_request_interceptor.h"
#if BUILDFLAG(INTEGRATED_MODE)
#include "components/cronet/android/cronet_integrated_mode_state.h"
#endif
using base::android::JavaParamRef;
using base::android::ScopedJavaLocalRef;
namespace {
// Helper method that takes a Java string that can be null, in which case it
// will get converted to an empty string.
std::string ConvertNullableJavaStringToUTF8(JNIEnv* env,
const JavaParamRef<jstring>& jstr) {
std::string str;
if (!jstr.is_null())
base::android::ConvertJavaStringToUTF8(env, jstr, &str);
return str;
}
} // namespace
namespace cronet {
CronetURLRequestContextAdapter::CronetURLRequestContextAdapter(
std::unique_ptr<URLRequestContextConfig> context_config) {
// Create context and pass ownership of |this| (self) to the context.
std::unique_ptr<CronetURLRequestContextAdapter> self(this);
#if BUILDFLAG(INTEGRATED_MODE)
// Create CronetURLRequestContext running in integrated network task runner.
context_ =
new CronetURLRequestContext(std::move(context_config), std::move(self),
GetIntegratedModeNetworkTaskRunner());
#else
context_ =
new CronetURLRequestContext(std::move(context_config), std::move(self));
#endif
}
CronetURLRequestContextAdapter::~CronetURLRequestContextAdapter() = default;
void CronetURLRequestContextAdapter::InitRequestContextOnInitThread(
JNIEnv* env,
const JavaParamRef<jobject>& jcaller) {
jcronet_url_request_context_.Reset(env, jcaller);
context_->InitRequestContextOnInitThread();
}
void CronetURLRequestContextAdapter::ConfigureNetworkQualityEstimatorForTesting(
JNIEnv* env,
const JavaParamRef<jobject>& jcaller,
jboolean use_local_host_requests,
jboolean use_smaller_responses,
jboolean disable_offline_check) {
context_->ConfigureNetworkQualityEstimatorForTesting(
use_local_host_requests == JNI_TRUE, use_smaller_responses == JNI_TRUE,
disable_offline_check == JNI_TRUE);
}
void CronetURLRequestContextAdapter::ProvideRTTObservations(
JNIEnv* env,
const JavaParamRef<jobject>& jcaller,
bool should) {
context_->ProvideRTTObservations(should == JNI_TRUE);
}
void CronetURLRequestContextAdapter::ProvideThroughputObservations(
JNIEnv* env,
const JavaParamRef<jobject>& jcaller,
bool should) {
context_->ProvideThroughputObservations(should == JNI_TRUE);
}
void CronetURLRequestContextAdapter::OnInitNetworkThread() {
JNIEnv* env = base::android::AttachCurrentThread();
Java_CronetUrlRequestContext_initNetworkThread(env,
jcronet_url_request_context_);
}
void CronetURLRequestContextAdapter::OnDestroyNetworkThread() {
// The |context_| is destroyed.
context_ = nullptr;
}
void CronetURLRequestContextAdapter::OnEffectiveConnectionTypeChanged(
net::EffectiveConnectionType effective_connection_type) {
Java_CronetUrlRequestContext_onEffectiveConnectionTypeChanged(
base::android::AttachCurrentThread(), jcronet_url_request_context_,
effective_connection_type);
}
void CronetURLRequestContextAdapter::OnRTTOrThroughputEstimatesComputed(
int32_t http_rtt_ms,
int32_t transport_rtt_ms,
int32_t downstream_throughput_kbps) {
Java_CronetUrlRequestContext_onRTTOrThroughputEstimatesComputed(
base::android::AttachCurrentThread(), jcronet_url_request_context_,
http_rtt_ms, transport_rtt_ms, downstream_throughput_kbps);
}
void CronetURLRequestContextAdapter::OnRTTObservation(
int32_t rtt_ms,
int32_t timestamp_ms,
net::NetworkQualityObservationSource source) {
Java_CronetUrlRequestContext_onRttObservation(
base::android::AttachCurrentThread(), jcronet_url_request_context_,
rtt_ms, timestamp_ms, source);
}
void CronetURLRequestContextAdapter::OnThroughputObservation(
int32_t throughput_kbps,
int32_t timestamp_ms,
net::NetworkQualityObservationSource source) {
Java_CronetUrlRequestContext_onThroughputObservation(
base::android::AttachCurrentThread(), jcronet_url_request_context_,
throughput_kbps, timestamp_ms, source);
}
void CronetURLRequestContextAdapter::OnStopNetLogCompleted() {
Java_CronetUrlRequestContext_stopNetLogCompleted(
base::android::AttachCurrentThread(), jcronet_url_request_context_);
}
void CronetURLRequestContextAdapter::Destroy(
JNIEnv* env,
const JavaParamRef<jobject>& jcaller) {
// Deleting |context_| on client thread will post cleanup onto network thread,
// which will in turn delete |this| on network thread.
delete context_;
}
net::URLRequestContext* CronetURLRequestContextAdapter::GetURLRequestContext() {
return context_->GetURLRequestContext();
}
void CronetURLRequestContextAdapter::PostTaskToNetworkThread(
const base::Location& posted_from,
base::OnceClosure callback) {
context_->PostTaskToNetworkThread(posted_from, std::move(callback));
}
bool CronetURLRequestContextAdapter::IsOnNetworkThread() const {
return context_->IsOnNetworkThread();
}
bool CronetURLRequestContextAdapter::StartNetLogToFile(
JNIEnv* env,
const JavaParamRef<jobject>& jcaller,
const JavaParamRef<jstring>& jfile_name,
jboolean jlog_all) {
std::string file_name(
base::android::ConvertJavaStringToUTF8(env, jfile_name));
return context_->StartNetLogToFile(file_name, jlog_all == JNI_TRUE);
}
void CronetURLRequestContextAdapter::StartNetLogToDisk(
JNIEnv* env,
const JavaParamRef<jobject>& jcaller,
const JavaParamRef<jstring>& jdir_name,
jboolean jlog_all,
jint jmax_size) {
std::string dir_name(base::android::ConvertJavaStringToUTF8(env, jdir_name));
context_->StartNetLogToDisk(dir_name, jlog_all == JNI_TRUE, jmax_size);
}
void CronetURLRequestContextAdapter::StopNetLog(
JNIEnv* env,
const JavaParamRef<jobject>& jcaller) {
context_->StopNetLog();
}
int CronetURLRequestContextAdapter::default_load_flags() const {
return context_->default_load_flags();
}
// Create a URLRequestContextConfig from the given parameters.
static jlong JNI_CronetUrlRequestContext_CreateRequestContextConfig(
JNIEnv* env,
const JavaParamRef<jstring>& juser_agent,
const JavaParamRef<jstring>& jstorage_path,
jboolean jquic_enabled,
const JavaParamRef<jstring>& jquic_default_user_agent_id,
jboolean jhttp2_enabled,
jboolean jbrotli_enabled,
jboolean jdisable_cache,
jint jhttp_cache_mode,
jlong jhttp_cache_max_size,
const JavaParamRef<jstring>& jexperimental_quic_connection_options,
jlong jmock_cert_verifier,
jboolean jenable_network_quality_estimator,
jboolean jbypass_public_key_pinning_for_local_trust_anchors,
jint jnetwork_thread_priority) {
return reinterpret_cast<jlong>(new URLRequestContextConfig(
jquic_enabled,
ConvertNullableJavaStringToUTF8(env, jquic_default_user_agent_id),
jhttp2_enabled, jbrotli_enabled,
static_cast<URLRequestContextConfig::HttpCacheType>(jhttp_cache_mode),
jhttp_cache_max_size, jdisable_cache,
ConvertNullableJavaStringToUTF8(env, jstorage_path),
/* accept_languages */ std::string(),
ConvertNullableJavaStringToUTF8(env, juser_agent),
ConvertNullableJavaStringToUTF8(env,
jexperimental_quic_connection_options),
base::WrapUnique(
reinterpret_cast<net::CertVerifier*>(jmock_cert_verifier)),
jenable_network_quality_estimator,
jbypass_public_key_pinning_for_local_trust_anchors,
jnetwork_thread_priority >= -20 && jnetwork_thread_priority <= 19
? base::Optional<double>(jnetwork_thread_priority)
: base::Optional<double>()));
}
// Add a QUIC hint to a URLRequestContextConfig.
static void JNI_CronetUrlRequestContext_AddQuicHint(
JNIEnv* env,
jlong jurl_request_context_config,
const JavaParamRef<jstring>& jhost,
jint jport,
jint jalternate_port) {
URLRequestContextConfig* config =
reinterpret_cast<URLRequestContextConfig*>(jurl_request_context_config);
config->quic_hints.push_back(
std::make_unique<URLRequestContextConfig::QuicHint>(
base::android::ConvertJavaStringToUTF8(env, jhost), jport,
jalternate_port));
}
// Add a public key pin to URLRequestContextConfig.
// |jhost| is the host to apply the pin to.
// |jhashes| is an array of jbyte[32] representing SHA256 key hashes.
// |jinclude_subdomains| indicates if pin should be applied to subdomains.
// |jexpiration_time| is the time that the pin expires, in milliseconds since
// Jan. 1, 1970, midnight GMT.
static void JNI_CronetUrlRequestContext_AddPkp(
JNIEnv* env,
jlong jurl_request_context_config,
const JavaParamRef<jstring>& jhost,
const JavaParamRef<jobjectArray>& jhashes,
jboolean jinclude_subdomains,
jlong jexpiration_time) {
URLRequestContextConfig* config =
reinterpret_cast<URLRequestContextConfig*>(jurl_request_context_config);
std::unique_ptr<URLRequestContextConfig::Pkp> pkp(
new URLRequestContextConfig::Pkp(
base::android::ConvertJavaStringToUTF8(env, jhost),
jinclude_subdomains,
base::Time::UnixEpoch() +
base::TimeDelta::FromMilliseconds(jexpiration_time)));
for (auto bytes_array : jhashes.ReadElements<jbyteArray>()) {
static_assert(std::is_pod<net::SHA256HashValue>::value,
"net::SHA256HashValue is not POD");
static_assert(sizeof(net::SHA256HashValue) * CHAR_BIT == 256,
"net::SHA256HashValue contains overhead");
if (env->GetArrayLength(bytes_array.obj()) !=
sizeof(net::SHA256HashValue)) {
LOG(ERROR) << "Unable to add public key hash value.";
continue;
}
jbyte* bytes = env->GetByteArrayElements(bytes_array.obj(), nullptr);
net::HashValue hash(*reinterpret_cast<net::SHA256HashValue*>(bytes));
pkp->pin_hashes.push_back(hash);
env->ReleaseByteArrayElements(bytes_array.obj(), bytes, JNI_ABORT);
}
config->pkp_list.push_back(std::move(pkp));
}
// Creates RequestContextAdater if config is valid URLRequestContextConfig,
// returns 0 otherwise.
static jlong JNI_CronetUrlRequestContext_CreateRequestContextAdapter(
JNIEnv* env,
jlong jconfig) {
std::unique_ptr<URLRequestContextConfig> context_config(
reinterpret_cast<URLRequestContextConfig*>(jconfig));
CronetURLRequestContextAdapter* context_adapter =
new CronetURLRequestContextAdapter(std::move(context_config));
return reinterpret_cast<jlong>(context_adapter);
}
static jint JNI_CronetUrlRequestContext_SetMinLogLevel(
JNIEnv* env,
jint jlog_level) {
jint old_log_level = static_cast<jint>(logging::GetMinLogLevel());
// MinLogLevel is global, shared by all URLRequestContexts.
logging::SetMinLogLevel(static_cast<int>(jlog_level));
return old_log_level;
}
static ScopedJavaLocalRef<jbyteArray>
JNI_CronetUrlRequestContext_GetHistogramDeltas(JNIEnv* env) {
std::vector<uint8_t> data;
if (!HistogramManager::GetInstance()->GetDeltas(&data))
return ScopedJavaLocalRef<jbyteArray>();
return base::android::ToJavaByteArray(env, &data[0], data.size());
}
} // namespace cronet