// Copyright 2014 The Chromium Authors
// 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_context_adapter.h"
#include "components/cronet/android/proto/request_context_config.pb.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/files/file_path.h"
#include "base/files/file_util.h"
#include "base/files/scoped_file.h"
#include "base/functional/bind.h"
#include "base/functional/callback.h"
#include "base/lazy_instance.h"
#include "base/logging.h"
#include "base/memory/ptr_util.h"
#include "base/metrics/histogram_macros.h"
#include "base/task/single_thread_task_runner.h"
#include "base/threading/thread_restrictions.h"
#include "base/time/time.h"
#include "base/values.h"
#include "components/cronet/android/cronet_library_loader.h"
#include "components/cronet/cronet_prefs_manager.h"
#include "components/cronet/host_cache_persistence_manager.h"
#include "components/cronet/url_request_context_config.h"
#include "components/metrics/library_support/histogram_manager.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/quiche/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"
// Must come after all headers that specialize FromJniType() / ToJniType().
#include "components/cronet/android/cronet_jni_headers/CronetUrlRequestContext_jni.h"
using base::android::JavaParamRef;
using base::android::ScopedJavaLocalRef;
namespace cronet {
CronetContextAdapter::CronetContextAdapter(
std::unique_ptr<URLRequestContextConfig> context_config) {
// Create context and pass ownership of |this| (self) to the context.
context_ = new CronetContext(std::move(context_config),
base::WrapUnique<CronetContextAdapter>(this));
}
CronetContextAdapter::~CronetContextAdapter() = default;
void CronetContextAdapter::InitRequestContextOnInitThread(
JNIEnv* env,
const JavaParamRef<jobject>& jcaller) {
jcronet_url_request_context_.Reset(env, jcaller);
context_->InitRequestContextOnInitThread();
}
void CronetContextAdapter::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);
}
bool CronetContextAdapter::URLRequestContextExistsForTesting(
net::handles::NetworkHandle network) {
return context_->URLRequestContextExistsForTesting(network); // IN-TEST
}
void CronetContextAdapter::ProvideRTTObservations(
JNIEnv* env,
const JavaParamRef<jobject>& jcaller,
bool should) {
context_->ProvideRTTObservations(should == JNI_TRUE);
}
void CronetContextAdapter::ProvideThroughputObservations(
JNIEnv* env,
const JavaParamRef<jobject>& jcaller,
bool should) {
context_->ProvideThroughputObservations(should == JNI_TRUE);
}
void CronetContextAdapter::OnInitNetworkThread() {
JNIEnv* env = base::android::AttachCurrentThread();
Java_CronetUrlRequestContext_initNetworkThread(env,
jcronet_url_request_context_);
}
void CronetContextAdapter::OnDestroyNetworkThread() {
// The |context_| is destroyed.
context_ = nullptr;
}
void CronetContextAdapter::OnEffectiveConnectionTypeChanged(
net::EffectiveConnectionType effective_connection_type) {
Java_CronetUrlRequestContext_onEffectiveConnectionTypeChanged(
base::android::AttachCurrentThread(), jcronet_url_request_context_,
effective_connection_type);
}
void CronetContextAdapter::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 CronetContextAdapter::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 CronetContextAdapter::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 CronetContextAdapter::OnStopNetLogCompleted() {
Java_CronetUrlRequestContext_stopNetLogCompleted(
base::android::AttachCurrentThread(), jcronet_url_request_context_);
}
void CronetContextAdapter::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* CronetContextAdapter::GetURLRequestContext(
net::handles::NetworkHandle network) {
return context_->GetURLRequestContext(network);
}
void CronetContextAdapter::PostTaskToNetworkThread(
const base::Location& posted_from,
base::OnceClosure callback) {
context_->PostTaskToNetworkThread(posted_from, std::move(callback));
}
bool CronetContextAdapter::IsOnNetworkThread() const {
return context_->IsOnNetworkThread();
}
bool CronetContextAdapter::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 CronetContextAdapter::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 CronetContextAdapter::StopNetLog(JNIEnv* env,
const JavaParamRef<jobject>& jcaller) {
context_->StopNetLog();
}
void CronetContextAdapter::FlushWritePropertiesForTesting(
JNIEnv* env,
const base::android::JavaParamRef<jobject>& jcaller) {
context_->FlushWritePropertiesForTesting(); // IN-TEST
}
int CronetContextAdapter::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<jbyteArray>& javaSerializedProto) {
const int serializedProtoLength = env->GetArrayLength(javaSerializedProto);
org::chromium::net::RequestContextConfigOptions configOptions;
std::vector<uint8_t> serializedProto;
base::android::JavaByteArrayToByteVector(env, javaSerializedProto,
&serializedProto);
if (!configOptions.ParseFromArray(serializedProto.data(),
serializedProtoLength)) {
return 0;
}
std::unique_ptr<URLRequestContextConfig> url_request_context_config =
URLRequestContextConfig::CreateURLRequestContextConfig(
configOptions.quic_enabled(), configOptions.http2_enabled(),
configOptions.brotli_enabled(),
static_cast<URLRequestContextConfig::HttpCacheType>(
configOptions.http_cache_mode()),
configOptions.http_cache_max_size(), configOptions.disable_cache(),
configOptions.storage_path(),
/* accept_languages */ std::string(), configOptions.user_agent(),
configOptions.experimental_options(),
base::WrapUnique(reinterpret_cast<net::CertVerifier*>(
configOptions.mock_cert_verifier())),
configOptions.enable_network_quality_estimator(),
configOptions.bypass_public_key_pinning_for_local_trust_anchors(),
configOptions.network_thread_priority() >= -20 &&
configOptions.network_thread_priority() <= 19
? std::optional<int>(configOptions.network_thread_priority())
: std::optional<int>());
return reinterpret_cast<jlong>(url_request_context_config.release());
}
// 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::Milliseconds(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));
CronetContextAdapter* context_adapter =
new CronetContextAdapter(std::move(context_config));
return reinterpret_cast<jlong>(context_adapter);
}
static ScopedJavaLocalRef<jbyteArray>
JNI_CronetUrlRequestContext_GetHistogramDeltas(JNIEnv* env) {
std::vector<uint8_t> data;
if (!metrics::HistogramManager::GetInstance()->GetDeltas(&data))
return ScopedJavaLocalRef<jbyteArray>();
return base::android::ToJavaByteArray(env, data);
}
} // namespace cronet