summaryrefslogtreecommitdiff
path: root/chromium/components/variations/service/variations_field_trial_creator.cc
diff options
context:
space:
mode:
authorAllan Sandfeld Jensen <allan.jensen@qt.io>2017-09-18 14:34:04 +0200
committerAllan Sandfeld Jensen <allan.jensen@qt.io>2017-10-04 11:15:27 +0000
commite6430e577f105ad8813c92e75c54660c4985026e (patch)
tree88115e5d1fb471fea807111924dcccbeadbf9e4f /chromium/components/variations/service/variations_field_trial_creator.cc
parent53d399fe6415a96ea6986ec0d402a9c07da72453 (diff)
downloadqtwebengine-chromium-e6430e577f105ad8813c92e75c54660c4985026e.tar.gz
BASELINE: Update Chromium to 61.0.3163.99
Change-Id: I8452f34574d88ca2b27af9bd56fc9ff3f16b1367 Reviewed-by: Alexandru Croitor <alexandru.croitor@qt.io>
Diffstat (limited to 'chromium/components/variations/service/variations_field_trial_creator.cc')
-rw-r--r--chromium/components/variations/service/variations_field_trial_creator.cc328
1 files changed, 328 insertions, 0 deletions
diff --git a/chromium/components/variations/service/variations_field_trial_creator.cc b/chromium/components/variations/service/variations_field_trial_creator.cc
new file mode 100644
index 00000000000..d01415249b3
--- /dev/null
+++ b/chromium/components/variations/service/variations_field_trial_creator.cc
@@ -0,0 +1,328 @@
+// Copyright (c) 2012 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/variations/service/variations_field_trial_creator.h"
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <utility>
+#include <vector>
+
+#include "base/build_time.h"
+#include "base/command_line.h"
+#include "base/metrics/histogram_macros.h"
+#include "base/metrics/sparse_histogram.h"
+#include "base/sys_info.h"
+#include "base/version.h"
+#include "build/build_config.h"
+#include "components/prefs/pref_service.h"
+#include "components/variations/pref_names.h"
+#include "components/variations/proto/variations_seed.pb.h"
+#include "components/variations/service/variations_service_client.h"
+#include "components/variations/variations_seed_processor.h"
+#include "components/variations/variations_switches.h"
+#include "ui/base/device_form_factor.h"
+
+namespace variations {
+
+// Maximum age permitted for a variations seed, in days.
+const int kMaxVariationsSeedAgeDays = 30;
+
+enum VariationsSeedExpiry {
+ VARIATIONS_SEED_EXPIRY_NOT_EXPIRED,
+ VARIATIONS_SEED_EXPIRY_FETCH_TIME_MISSING,
+ VARIATIONS_SEED_EXPIRY_EXPIRED,
+ VARIATIONS_SEED_EXPIRY_ENUM_SIZE,
+};
+
+// Gets current form factor and converts it from enum DeviceFormFactor to enum
+// Study_FormFactor.
+Study::FormFactor GetCurrentFormFactor() {
+ switch (ui::GetDeviceFormFactor()) {
+ case ui::DEVICE_FORM_FACTOR_PHONE:
+ return Study::PHONE;
+ case ui::DEVICE_FORM_FACTOR_TABLET:
+ return Study::TABLET;
+ case ui::DEVICE_FORM_FACTOR_DESKTOP:
+ return Study::DESKTOP;
+ }
+ NOTREACHED();
+ return Study::DESKTOP;
+}
+
+// Gets the hardware class and returns it as a string. This returns an empty
+// string if the client is not ChromeOS.
+std::string GetHardwareClass() {
+#if defined(OS_CHROMEOS)
+ return base::SysInfo::GetLsbReleaseBoard();
+#endif // OS_CHROMEOS
+ return std::string();
+}
+
+// Returns the date that should be used by the VariationsSeedProcessor to do
+// expiry and start date checks.
+base::Time GetReferenceDateForExpiryChecks(PrefService* local_state) {
+ const int64_t date_value = local_state->GetInt64(prefs::kVariationsSeedDate);
+ const base::Time seed_date = base::Time::FromInternalValue(date_value);
+ const base::Time build_time = base::GetBuildTime();
+ // Use the build time for date checks if either the seed date is invalid or
+ // the build time is newer than the seed date.
+ base::Time reference_date = seed_date;
+ if (seed_date.is_null() || seed_date < build_time)
+ reference_date = build_time;
+ return reference_date;
+}
+
+// Wrapper around channel checking, used to enable channel mocking for
+// testing. If the current browser channel is not UNKNOWN, this will return
+// that channel value. Otherwise, if the fake channel flag is provided, this
+// will return the fake channel. Failing that, this will return the UNKNOWN
+// channel.
+Study::Channel GetChannelForVariations(version_info::Channel product_channel) {
+ switch (product_channel) {
+ case version_info::Channel::CANARY:
+ return Study::CANARY;
+ case version_info::Channel::DEV:
+ return Study::DEV;
+ case version_info::Channel::BETA:
+ return Study::BETA;
+ case version_info::Channel::STABLE:
+ return Study::STABLE;
+ case version_info::Channel::UNKNOWN:
+ break;
+ }
+ const std::string forced_channel =
+ base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
+ switches::kFakeVariationsChannel);
+ if (forced_channel == "stable")
+ return Study::STABLE;
+ if (forced_channel == "beta")
+ return Study::BETA;
+ if (forced_channel == "dev")
+ return Study::DEV;
+ if (forced_channel == "canary")
+ return Study::CANARY;
+ DVLOG(1) << "Invalid channel provided: " << forced_channel;
+ return Study::UNKNOWN;
+}
+
+// Records UMA histogram with the result of the variations seed expiry check.
+void RecordCreateTrialsSeedExpiry(VariationsSeedExpiry expiry_check_result) {
+ UMA_HISTOGRAM_ENUMERATION("Variations.CreateTrials.SeedExpiry",
+ expiry_check_result,
+ VARIATIONS_SEED_EXPIRY_ENUM_SIZE);
+}
+
+VariationsFieldTrialCreator::VariationsFieldTrialCreator(
+ PrefService* local_state,
+ VariationsServiceClient* client,
+ const UIStringOverrider& ui_string_overrider)
+ : client_(client),
+ ui_string_overrider_(ui_string_overrider),
+ seed_store_(local_state),
+ create_trials_from_seed_called_(false) {}
+
+VariationsFieldTrialCreator::~VariationsFieldTrialCreator() {}
+
+std::string VariationsFieldTrialCreator::GetLatestCountry() const {
+ const std::string override_country =
+ base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
+ switches::kVariationsOverrideCountry);
+ return !override_country.empty()
+ ? override_country
+ : local_state()->GetString(prefs::kVariationsCountry);
+}
+
+bool VariationsFieldTrialCreator::CreateTrialsFromSeed(
+ std::unique_ptr<const base::FieldTrial::EntropyProvider>
+ low_entropy_provider,
+ base::FeatureList* feature_list) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ CHECK(!create_trials_from_seed_called_);
+
+ create_trials_from_seed_called_ = true;
+
+ VariationsSeed seed;
+ if (!LoadSeed(&seed))
+ return false;
+
+ const int64_t last_fetch_time_internal =
+ local_state()->GetInt64(prefs::kVariationsLastFetchTime);
+ const base::Time last_fetch_time =
+ base::Time::FromInternalValue(last_fetch_time_internal);
+ if (last_fetch_time.is_null()) {
+ // If the last fetch time is missing and we have a seed, then this must be
+ // the first run of Chrome. Store the current time as the last fetch time.
+ RecordLastFetchTime();
+ RecordCreateTrialsSeedExpiry(VARIATIONS_SEED_EXPIRY_FETCH_TIME_MISSING);
+ } else {
+ // Reject the seed if it is more than 30 days old.
+ const base::TimeDelta seed_age = base::Time::Now() - last_fetch_time;
+ if (seed_age.InDays() > kMaxVariationsSeedAgeDays) {
+ RecordCreateTrialsSeedExpiry(VARIATIONS_SEED_EXPIRY_EXPIRED);
+ return false;
+ }
+ RecordCreateTrialsSeedExpiry(VARIATIONS_SEED_EXPIRY_NOT_EXPIRED);
+ }
+
+ const base::Version current_version(version_info::GetVersionNumber());
+ if (!current_version.IsValid())
+ return false;
+
+ std::unique_ptr<ClientFilterableState> client_state =
+ GetClientFilterableStateForVersion(current_version);
+
+ // Note that passing |&ui_string_overrider_| via base::Unretained below is
+ // safe because the callback is executed synchronously. It is not possible
+ // to pass UIStringOverrider itself to VariationSeedProcessor as variations
+ // components should not depends on //ui/base.
+ VariationsSeedProcessor().CreateTrialsFromSeed(
+ seed, *client_state,
+ base::Bind(&UIStringOverrider::OverrideUIString,
+ base::Unretained(&ui_string_overrider_)),
+ low_entropy_provider.get(), feature_list);
+
+ const base::Time now = base::Time::Now();
+
+ // Log the "freshness" of the seed that was just used. The freshness is the
+ // time between the last successful seed download and now.
+ if (!last_fetch_time.is_null()) {
+ const base::TimeDelta delta = now - last_fetch_time;
+ // Log the value in number of minutes.
+ UMA_HISTOGRAM_CUSTOM_COUNTS("Variations.SeedFreshness", delta.InMinutes(),
+ 1, base::TimeDelta::FromDays(30).InMinutes(),
+ 50);
+ }
+
+ return true;
+}
+
+void VariationsFieldTrialCreator::SetCreateTrialsFromSeedCalledForTesting(
+ bool called) {
+ create_trials_from_seed_called_ = called;
+}
+
+std::unique_ptr<ClientFilterableState>
+VariationsFieldTrialCreator::GetClientFilterableStateForVersion(
+ const base::Version& version) {
+ std::unique_ptr<ClientFilterableState> state =
+ base::MakeUnique<ClientFilterableState>();
+ state->locale = client_->GetApplicationLocale();
+ state->reference_date = GetReferenceDateForExpiryChecks(local_state());
+ state->version = version;
+ state->channel = GetChannelForVariations(client_->GetChannel());
+ state->form_factor = GetCurrentFormFactor();
+ state->platform = ClientFilterableState::GetCurrentPlatform();
+ state->hardware_class = GetHardwareClass();
+#if defined(OS_ANDROID)
+ // This is set on Android only currently, because the IsLowEndDevice() API
+ // on other platforms has no intrinsic meaning outside of a field trial that
+ // controls its value. Since this is before server-side field trials are
+ // evaluated, that field trial would not be able to apply for this case.
+ state->is_low_end_device = base::SysInfo::IsLowEndDevice();
+#endif
+ state->session_consistency_country = GetLatestCountry();
+ state->permanent_consistency_country = LoadPermanentConsistencyCountry(
+ version, state->session_consistency_country);
+ return state;
+}
+
+std::string VariationsFieldTrialCreator::LoadPermanentConsistencyCountry(
+ const base::Version& version,
+ const std::string& latest_country) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ DCHECK(version.IsValid());
+
+ const std::string override_country =
+ base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
+ switches::kVariationsOverrideCountry);
+ if (!override_country.empty())
+ return override_country;
+
+ const base::ListValue* list_value =
+ local_state()->GetList(prefs::kVariationsPermanentConsistencyCountry);
+ std::string stored_version_string;
+ std::string stored_country;
+
+ // Determine if the saved pref value is present and valid.
+ const bool is_pref_empty = list_value->empty();
+ const bool is_pref_valid = list_value->GetSize() == 2 &&
+ list_value->GetString(0, &stored_version_string) &&
+ list_value->GetString(1, &stored_country) &&
+ base::Version(stored_version_string).IsValid();
+
+ // Determine if the version from the saved pref matches |version|.
+ const bool does_version_match =
+ is_pref_valid && version == base::Version(stored_version_string);
+
+ // Determine if the country in the saved pref matches the country in
+ // |latest_country|.
+ const bool does_country_match = is_pref_valid && !latest_country.empty() &&
+ stored_country == latest_country;
+
+ // Record a histogram for how the saved pref value compares to the current
+ // version and the country code in the variations seed.
+ LoadPermanentConsistencyCountryResult result;
+ if (is_pref_empty) {
+ result = !latest_country.empty() ? LOAD_COUNTRY_NO_PREF_HAS_SEED
+ : LOAD_COUNTRY_NO_PREF_NO_SEED;
+ } else if (!is_pref_valid) {
+ result = !latest_country.empty() ? LOAD_COUNTRY_INVALID_PREF_HAS_SEED
+ : LOAD_COUNTRY_INVALID_PREF_NO_SEED;
+ } else if (latest_country.empty()) {
+ result = does_version_match ? LOAD_COUNTRY_HAS_PREF_NO_SEED_VERSION_EQ
+ : LOAD_COUNTRY_HAS_PREF_NO_SEED_VERSION_NEQ;
+ } else if (does_version_match) {
+ result = does_country_match ? LOAD_COUNTRY_HAS_BOTH_VERSION_EQ_COUNTRY_EQ
+ : LOAD_COUNTRY_HAS_BOTH_VERSION_EQ_COUNTRY_NEQ;
+ } else {
+ result = does_country_match ? LOAD_COUNTRY_HAS_BOTH_VERSION_NEQ_COUNTRY_EQ
+ : LOAD_COUNTRY_HAS_BOTH_VERSION_NEQ_COUNTRY_NEQ;
+ }
+ UMA_HISTOGRAM_ENUMERATION("Variations.LoadPermanentConsistencyCountryResult",
+ result, LOAD_COUNTRY_MAX);
+
+ // Use the stored country if one is available and was fetched since the last
+ // time Chrome was updated.
+ if (does_version_match)
+ return stored_country;
+
+ if (latest_country.empty()) {
+ if (!is_pref_valid)
+ local_state()->ClearPref(prefs::kVariationsPermanentConsistencyCountry);
+ // If we've never received a country code from the server, use an empty
+ // country so that it won't pass any filters that specifically include
+ // countries, but so that it will pass any filters that specifically exclude
+ // countries.
+ return std::string();
+ }
+
+ // Otherwise, update the pref with the current Chrome version and country.
+ StorePermanentCountry(version, latest_country);
+ return latest_country;
+}
+
+void VariationsFieldTrialCreator::StorePermanentCountry(
+ const base::Version& version,
+ const std::string& country) {
+ base::ListValue new_list_value;
+ new_list_value.AppendString(version.GetString());
+ new_list_value.AppendString(country);
+ local_state()->Set(prefs::kVariationsPermanentConsistencyCountry,
+ new_list_value);
+}
+
+void VariationsFieldTrialCreator::RecordLastFetchTime() {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+ local_state()->SetInt64(prefs::kVariationsLastFetchTime,
+ base::Time::Now().ToInternalValue());
+}
+
+bool VariationsFieldTrialCreator::LoadSeed(VariationsSeed* seed) {
+ return seed_store_.LoadSeed(seed);
+}
+
+} // namespace variations