blob: a0b2217287a8dbcbf41912ed490cbed4a24b8b8a [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/data_reduction_proxy/core/browser/data_reduction_proxy_settings.h"
#include <utility>
#include "base/bind.h"
#include "base/command_line.h"
#include "base/feature_list.h"
#include "base/metrics/histogram_macros.h"
#include "base/time/clock.h"
#include "base/time/default_clock.h"
#include "build/build_config.h"
#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_config.h"
#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_io_data.h"
#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_service.h"
#include "components/data_reduction_proxy/core/common/data_reduction_proxy_features.h"
#include "components/data_reduction_proxy/core/common/data_reduction_proxy_params.h"
#include "components/data_reduction_proxy/core/common/data_reduction_proxy_pref_names.h"
#include "components/data_reduction_proxy/core/common/data_reduction_proxy_switches.h"
#include "components/prefs/pref_member.h"
#include "components/prefs/pref_service.h"
#include "net/base/network_change_notifier.h"
#include "net/http/http_request_headers.h"
#include "services/network/public/cpp/features.h"
namespace {
// Key of the UMA DataReductionProxy.StartupState histogram.
const char kUMAProxyStartupStateHistogram[] =
"DataReductionProxy.StartupState";
void RecordSettingsEnabledState(
data_reduction_proxy::DataReductionSettingsEnabledAction action) {
UMA_HISTOGRAM_ENUMERATION(
"DataReductionProxy.EnabledState", action,
data_reduction_proxy::DATA_REDUCTION_SETTINGS_ACTION_BOUNDARY);
}
// Record the number of days since data reduction proxy was enabled by the
// user.
void RecordDaysSinceEnabledMetric(int days_since_enabled) {
UMA_HISTOGRAM_CUSTOM_COUNTS("DataReductionProxy.DaysSinceEnabled",
days_since_enabled, 0, 365 * 10, 100);
}
} // namespace
namespace data_reduction_proxy {
DataReductionProxySettings::DataReductionProxySettings()
: unreachable_(false),
deferred_initialization_(false),
prefs_(nullptr),
config_(nullptr),
clock_(base::DefaultClock::GetInstance()) {}
DataReductionProxySettings::~DataReductionProxySettings() = default;
void DataReductionProxySettings::InitDataReductionProxySettings(
PrefService* prefs,
DataReductionProxyIOData* io_data,
std::unique_ptr<DataReductionProxyService> data_reduction_proxy_service) {
DCHECK(thread_checker_.CalledOnValidThread());
DCHECK(prefs);
DCHECK(io_data);
DCHECK(io_data->config());
DCHECK(data_reduction_proxy_service);
prefs_ = prefs;
config_ = io_data->config();
data_reduction_proxy_service_ = std::move(data_reduction_proxy_service);
data_reduction_proxy_service_->AddObserver(this);
RecordDataReductionInit();
registrar_.Init(prefs_);
registrar_.Add(
prefs::kDataSaverEnabled,
base::BindRepeating(&DataReductionProxySettings::OnProxyEnabledPrefChange,
base::Unretained(this)));
#if defined(OS_ANDROID)
if (IsDataSaverEnabledByUser(prefs_)) {
data_reduction_proxy_service_->compression_stats()
->SetDataUsageReportingEnabled(true);
}
#endif // defined(OS_ANDROID)
for (auto& observer : observers_)
observer.OnSettingsInitialized();
}
void DataReductionProxySettings::OnServiceInitialized() {
DCHECK(thread_checker_.CalledOnValidThread());
if (!deferred_initialization_)
return;
deferred_initialization_ = false;
// Technically, this is not "at startup", but this is the first chance that
// IO data objects can be called.
UpdateIOData(true);
if (proxy_config_client_) {
data_reduction_proxy_service_->SetCustomProxyConfigClient(
std::move(proxy_config_client_));
}
}
void DataReductionProxySettings::SetCallbackToRegisterSyntheticFieldTrial(
const SyntheticFieldTrialRegistrationCallback&
on_data_reduction_proxy_enabled) {
register_synthetic_field_trial_ = on_data_reduction_proxy_enabled;
RegisterDataReductionProxyFieldTrial();
}
// static
bool DataReductionProxySettings::IsDataSaverEnabledByUser(PrefService* prefs) {
if (params::ShouldForceEnableDataReductionProxy())
return true;
#if defined(OS_ANDROID)
return prefs && prefs->GetBoolean(prefs::kDataSaverEnabled);
#else
return false;
#endif
}
// static
void DataReductionProxySettings::SetDataSaverEnabledForTesting(
PrefService* prefs,
bool enabled) {
// Set the command line so that |IsDataSaverEnabledByUser| returns as expected
// on all platforms.
base::CommandLine* cmd = base::CommandLine::ForCurrentProcess();
if (enabled) {
cmd->AppendSwitch(switches::kEnableDataReductionProxy);
} else {
cmd->RemoveSwitch(switches::kEnableDataReductionProxy);
}
// Set the pref so that all the pref change callbacks run.
prefs->SetBoolean(prefs::kDataSaverEnabled, enabled);
}
bool DataReductionProxySettings::IsDataReductionProxyEnabled() const {
if (base::FeatureList::IsEnabled(network::features::kNetworkService) &&
!params::IsEnabledWithNetworkService()) {
return false;
}
return IsDataSaverEnabledByUser(GetOriginalProfilePrefs());
}
bool DataReductionProxySettings::CanUseDataReductionProxy(
const GURL& url) const {
return url.is_valid() && url.scheme() == url::kHttpScheme &&
IsDataReductionProxyEnabled();
}
bool DataReductionProxySettings::IsDataReductionProxyManaged() {
const PrefService::Preference* pref =
GetOriginalProfilePrefs()->FindPreference(prefs::kDataSaverEnabled);
return pref && pref->IsManaged();
}
void DataReductionProxySettings::SetDataReductionProxyEnabled(bool enabled) {
DCHECK(thread_checker_.CalledOnValidThread());
DCHECK(data_reduction_proxy_service_->compression_stats());
if (GetOriginalProfilePrefs()->GetBoolean(prefs::kDataSaverEnabled) !=
enabled) {
GetOriginalProfilePrefs()->SetBoolean(prefs::kDataSaverEnabled, enabled);
OnProxyEnabledPrefChange();
#if defined(OS_ANDROID)
data_reduction_proxy_service_->compression_stats()
->SetDataUsageReportingEnabled(enabled);
#endif // defined(OS_ANDROID)
}
}
int64_t DataReductionProxySettings::GetDataReductionLastUpdateTime() {
DCHECK(thread_checker_.CalledOnValidThread());
DCHECK(data_reduction_proxy_service_->compression_stats());
return
data_reduction_proxy_service_->compression_stats()->GetLastUpdateTime();
}
void DataReductionProxySettings::ClearDataSavingStatistics(
DataReductionProxySavingsClearedReason reason) {
DCHECK(thread_checker_.CalledOnValidThread());
DCHECK(data_reduction_proxy_service_->compression_stats());
data_reduction_proxy_service_->compression_stats()->ClearDataSavingStatistics(
reason);
}
int64_t DataReductionProxySettings::GetTotalHttpContentLengthSaved() {
DCHECK(thread_checker_.CalledOnValidThread());
return data_reduction_proxy_service_->compression_stats()
->GetHttpOriginalContentLength() -
data_reduction_proxy_service_->compression_stats()
->GetHttpReceivedContentLength();
}
void DataReductionProxySettings::SetUnreachable(bool unreachable) {
unreachable_ = unreachable;
}
bool DataReductionProxySettings::IsDataReductionProxyUnreachable() {
DCHECK(thread_checker_.CalledOnValidThread());
return unreachable_;
}
PrefService* DataReductionProxySettings::GetOriginalProfilePrefs() const {
DCHECK(thread_checker_.CalledOnValidThread());
return prefs_;
}
void DataReductionProxySettings::RegisterDataReductionProxyFieldTrial() {
register_synthetic_field_trial_.Run(
"SyntheticDataReductionProxySetting",
IsDataReductionProxyEnabled() ? "Enabled" : "Disabled");
}
void DataReductionProxySettings::OnProxyEnabledPrefChange() {
DCHECK(thread_checker_.CalledOnValidThread());
if (!register_synthetic_field_trial_.is_null()) {
RegisterDataReductionProxyFieldTrial();
}
MaybeActivateDataReductionProxy(false);
bool enabled = IsDataReductionProxyEnabled();
for (auto& observer : observers_)
observer.OnDataSaverEnabledChanged(enabled);
}
void DataReductionProxySettings::ResetDataReductionStatistics() {
DCHECK(thread_checker_.CalledOnValidThread());
DCHECK(data_reduction_proxy_service_->compression_stats());
data_reduction_proxy_service_->compression_stats()->ResetStatistics();
}
void DataReductionProxySettings::UpdateIOData(bool at_startup) {
data_reduction_proxy_service_->SetProxyPrefs(IsDataReductionProxyEnabled(),
at_startup);
}
void DataReductionProxySettings::MaybeActivateDataReductionProxy(
bool at_startup) {
DCHECK(thread_checker_.CalledOnValidThread());
PrefService* prefs = GetOriginalProfilePrefs();
// Do nothing if prefs have not been initialized. This allows unit testing
// of profile related code without having to initialize data reduction proxy
// related prefs.
if (!prefs)
return;
bool enabled = IsDataSaverEnabledByUser(prefs);
if (enabled && at_startup) {
// Record the number of days since data reduction proxy has been enabled.
int64_t last_enabled_time =
prefs->GetInt64(prefs::kDataReductionProxyLastEnabledTime);
if (last_enabled_time != 0) {
// Record the metric only if the time when data reduction proxy was
// enabled is available.
RecordDaysSinceEnabledMetric(
(clock_->Now() - base::Time::FromInternalValue(last_enabled_time))
.InDays());
}
int64_t last_savings_cleared_time = prefs->GetInt64(
prefs::kDataReductionProxySavingsClearedNegativeSystemClock);
if (last_savings_cleared_time != 0) {
int32_t days_since_savings_cleared =
(clock_->Now() -
base::Time::FromInternalValue(last_savings_cleared_time))
.InDays();
// Sample in the UMA histograms must be at least 1.
if (days_since_savings_cleared == 0)
days_since_savings_cleared = 1;
UMA_HISTOGRAM_CUSTOM_COUNTS(
"DataReductionProxy.DaysSinceSavingsCleared.NegativeSystemClock",
days_since_savings_cleared, 1, 365, 50);
}
}
if (enabled &&
!prefs->GetBoolean(prefs::kDataReductionProxyWasEnabledBefore)) {
prefs->SetBoolean(prefs::kDataReductionProxyWasEnabledBefore, true);
ResetDataReductionStatistics();
}
if (!at_startup) {
if (IsDataReductionProxyEnabled()) {
RecordSettingsEnabledState(DATA_REDUCTION_SETTINGS_ACTION_OFF_TO_ON);
// Data reduction proxy has been enabled by the user. Record the number of
// days since the data reduction proxy has been enabled as zero, and
// store the current time in the pref.
prefs->SetInt64(prefs::kDataReductionProxyLastEnabledTime,
clock_->Now().ToInternalValue());
RecordDaysSinceEnabledMetric(0);
} else {
RecordSettingsEnabledState(DATA_REDUCTION_SETTINGS_ACTION_ON_TO_OFF);
}
}
// Configure use of the data reduction proxy if it is enabled.
if (at_startup && !data_reduction_proxy_service_->Initialized())
deferred_initialization_ = true;
else
UpdateIOData(at_startup);
}
const net::HttpRequestHeaders&
DataReductionProxySettings::GetProxyRequestHeaders() const {
DCHECK(thread_checker_.CalledOnValidThread());
return proxy_request_headers_;
}
void DataReductionProxySettings::SetProxyRequestHeaders(
const net::HttpRequestHeaders& headers) {
DCHECK(thread_checker_.CalledOnValidThread());
proxy_request_headers_ = headers;
for (auto& observer : observers_)
observer.OnProxyRequestHeadersChanged(headers);
}
void DataReductionProxySettings::SetConfiguredProxies(
const net::ProxyList& proxies) {
DCHECK(thread_checker_.CalledOnValidThread());
configured_proxies_ = proxies;
}
void DataReductionProxySettings::SetProxiesForHttp(
const std::vector<DataReductionProxyServer>& proxies_for_http) {
DCHECK(thread_checker_.CalledOnValidThread());
proxies_for_http_ = proxies_for_http;
}
bool DataReductionProxySettings::IsConfiguredDataReductionProxy(
const net::ProxyServer& proxy_server) const {
if (proxy_server.is_direct() || !proxy_server.is_valid())
return false;
for (const auto& drp_proxy : configured_proxies_.GetAll()) {
if (drp_proxy.host_port_pair().Equals(proxy_server.host_port_pair()))
return true;
}
return false;
}
void DataReductionProxySettings::AddDataReductionProxySettingsObserver(
DataReductionProxySettingsObserver* observer) {
DCHECK(thread_checker_.CalledOnValidThread());
observers_.AddObserver(observer);
}
void DataReductionProxySettings::RemoveDataReductionProxySettingsObserver(
DataReductionProxySettingsObserver* observer) {
DCHECK(thread_checker_.CalledOnValidThread());
observers_.RemoveObserver(observer);
}
void DataReductionProxySettings::SetCustomProxyConfigClient(
network::mojom::CustomProxyConfigClientPtrInfo proxy_config_client) {
proxy_config_client_ = std::move(proxy_config_client);
}
// Metrics methods
void DataReductionProxySettings::RecordDataReductionInit() const {
DCHECK(thread_checker_.CalledOnValidThread());
RecordStartupState(IsDataReductionProxyEnabled() ? PROXY_ENABLED
: PROXY_DISABLED);
RecordStartupSavings();
}
void DataReductionProxySettings::RecordStartupState(
ProxyStartupState state) const {
UMA_HISTOGRAM_ENUMERATION(kUMAProxyStartupStateHistogram,
state,
PROXY_STARTUP_STATE_COUNT);
}
void DataReductionProxySettings::RecordStartupSavings() const {
// Minimum bytes the user should have browsed, for the data savings percent
// UMA to be recorded at startup.
const unsigned int kMinOriginalContentLengthBytes =
10 * 1024 * 1024; // 10 MB.
if (!IsDataReductionProxyEnabled())
return;
DCHECK(data_reduction_proxy_service_->compression_stats());
int64_t original_content_length =
data_reduction_proxy_service_->compression_stats()
->GetHttpOriginalContentLength();
int64_t received_content_length =
data_reduction_proxy_service_->compression_stats()
->GetHttpReceivedContentLength();
if (original_content_length < kMinOriginalContentLengthBytes)
return;
int savings_percent =
static_cast<int>(((original_content_length - received_content_length) /
(float)original_content_length) *
100.0);
if (savings_percent >= 0) {
UMA_HISTOGRAM_PERCENTAGE("DataReductionProxy.StartupSavingsPercent",
savings_percent > 0 ? savings_percent : 0);
}
if (savings_percent < 0) {
UMA_HISTOGRAM_PERCENTAGE("DataReductionProxy.StartupNegativeSavingsPercent",
-savings_percent);
}
}
ContentLengthList
DataReductionProxySettings::GetDailyContentLengths(const char* pref_name) {
DCHECK(thread_checker_.CalledOnValidThread());
DCHECK(data_reduction_proxy_service_->compression_stats());
return data_reduction_proxy_service_->compression_stats()->
GetDailyContentLengths(pref_name);
}
void DataReductionProxySettings::GetContentLengths(
unsigned int days,
int64_t* original_content_length,
int64_t* received_content_length,
int64_t* last_update_time) {
DCHECK(thread_checker_.CalledOnValidThread());
DCHECK(data_reduction_proxy_service_->compression_stats());
data_reduction_proxy_service_->compression_stats()->GetContentLengths(
days, original_content_length, received_content_length, last_update_time);
}
} // namespace data_reduction_proxy