blob: 6ce9d5914485a9f3bfe116080d3db5a9fb3feed0 [file] [log] [blame]
// Copyright 2016 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/metrics/data_use_tracker.h"
#include <memory>
#include <string>
#include "base/i18n/time_formatting.h"
#include "base/values.h"
#include "build/build_config.h"
#include "components/metrics/metrics_pref_names.h"
#include "components/prefs/scoped_user_pref_update.h"
namespace metrics {
namespace {
// Default weekly quota and allowed UMA ratio for UMA log uploads for Android.
// These defaults will not be used for non-Android as |DataUseTracker| will not
// be initialized.
const int kDefaultUMAWeeklyQuotaBytes = 200 * 1024; // 200KB.
const double kDefaultUMARatio = 0.05;
} // namespace
DataUseTracker::DataUseTracker(PrefService* local_state)
: local_state_(local_state) {}
DataUseTracker::~DataUseTracker() {}
// static
std::unique_ptr<DataUseTracker> DataUseTracker::Create(
PrefService* local_state) {
std::unique_ptr<DataUseTracker> data_use_tracker;
// Instantiate DataUseTracker only on Android. UpdateMetricsUsagePrefs() honors
// this rule too.
#if BUILDFLAG(IS_ANDROID)
data_use_tracker = std::make_unique<DataUseTracker>(local_state);
#endif
return data_use_tracker;
}
// static
void DataUseTracker::RegisterPrefs(PrefRegistrySimple* registry) {
registry->RegisterDictionaryPref(metrics::prefs::kUserCellDataUse);
registry->RegisterDictionaryPref(metrics::prefs::kUmaCellDataUse);
}
// static
void DataUseTracker::UpdateMetricsUsagePrefs(int message_size,
bool is_cellular,
bool is_metrics_service_usage,
PrefService* local_state) {
// Instantiate DataUseTracker only on Android. Create() honors this rule too.
#if BUILDFLAG(IS_ANDROID)
metrics::DataUseTracker tracker(local_state);
tracker.UpdateMetricsUsagePrefsInternal(message_size, is_cellular,
is_metrics_service_usage);
#endif // BUILDFLAG(IS_ANDROID)
}
void DataUseTracker::UpdateMetricsUsagePrefsInternal(
int message_size,
bool is_cellular,
bool is_metrics_service_usage) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (!is_cellular)
return;
UpdateUsagePref(prefs::kUserCellDataUse, message_size);
// TODO(holte): Consider adding seperate tracking for UKM.
if (is_metrics_service_usage)
UpdateUsagePref(prefs::kUmaCellDataUse, message_size);
}
bool DataUseTracker::ShouldUploadLogOnCellular(int log_bytes) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
RemoveExpiredEntries();
int uma_total_data_use = ComputeTotalDataUse(prefs::kUmaCellDataUse);
int new_uma_total_data_use = log_bytes + uma_total_data_use;
// If the new log doesn't increase the total UMA traffic to be above the
// allowed quota then the log should be uploaded.
if (new_uma_total_data_use <= kDefaultUMAWeeklyQuotaBytes) {
return true;
}
int user_total_data_use = ComputeTotalDataUse(prefs::kUserCellDataUse);
// If after adding the new log the uma ratio is still under the allowed ratio
// then the log should be uploaded and vice versa.
return new_uma_total_data_use /
static_cast<double>(log_bytes + user_total_data_use) <=
kDefaultUMARatio;
}
void DataUseTracker::UpdateUsagePref(const std::string& pref_name,
int message_size) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
ScopedDictPrefUpdate pref_updater(local_state_, pref_name);
std::string todays_key = GetCurrentMeasurementDateAsString();
const base::Value::Dict& user_pref_dict = local_state_->GetDict(pref_name);
int todays_traffic = user_pref_dict.FindInt(todays_key).value_or(0);
pref_updater->Set(todays_key, todays_traffic + message_size);
}
void DataUseTracker::RemoveExpiredEntries() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
RemoveExpiredEntriesForPref(prefs::kUmaCellDataUse);
RemoveExpiredEntriesForPref(prefs::kUserCellDataUse);
}
void DataUseTracker::RemoveExpiredEntriesForPref(const std::string& pref_name) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
const base::Value::Dict& user_pref_dict = local_state_->GetDict(pref_name);
const base::Time current_date = GetCurrentMeasurementDate();
const base::Time week_ago = current_date - base::Days(7);
base::Value::Dict user_pref_new_dict;
for (const auto it : user_pref_dict) {
base::Time key_date;
if (base::Time::FromUTCString(it.first.c_str(), &key_date) &&
key_date > week_ago) {
user_pref_new_dict.Set(it.first, it.second.Clone());
}
}
local_state_->SetDict(pref_name, std::move(user_pref_new_dict));
}
// Note: We compute total data use regardless of what is the current date. In
// scenario when user travels back in time zone and current date becomes earlier
// than latest registered date in perf, we still count that in total use as user
// actually used that data.
int DataUseTracker::ComputeTotalDataUse(const std::string& pref_name) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
int total_data_use = 0;
const base::Value::Dict& pref_dict = local_state_->GetDict(pref_name);
for (const auto it : pref_dict) {
total_data_use += it.second.GetIfInt().value_or(0);
}
return total_data_use;
}
base::Time DataUseTracker::GetCurrentMeasurementDate() const {
return base::Time::Now().LocalMidnight();
}
std::string DataUseTracker::GetCurrentMeasurementDateAsString() const {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return base::UnlocalizedTimeFormatWithPattern(GetCurrentMeasurementDate(),
"yyyy-MM-dd");
}
} // namespace metrics