blob: 11618d4af83ad91bfade0c4a178174117204dcee [file] [log] [blame]
// Copyright 2017 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef COMPONENTS_VARIATIONS_SERVICE_VARIATIONS_FIELD_TRIAL_CREATOR_H_
#define COMPONENTS_VARIATIONS_SERVICE_VARIATIONS_FIELD_TRIAL_CREATOR_H_
#include <cstdint>
#include <memory>
#include <optional>
#include <string>
#include <unordered_map>
#include <vector>
#include "base/compiler_specific.h"
#include "base/containers/flat_set.h"
#include "base/memory/raw_ptr.h"
#include "base/metrics/field_trial.h"
#include "base/time/time.h"
#include "base/version_info/channel.h"
#include "build/build_config.h"
#include "components/variations/client_filterable_state.h"
#include "components/variations/metrics.h"
#include "components/variations/proto/study.pb.h"
#include "components/variations/seed_response.h"
#include "components/variations/service/buildflags.h"
#include "components/variations/service/safe_seed_manager.h"
#include "components/variations/service/ui_string_overrider.h"
#include "components/variations/service/variations_service_client.h"
#include "components/variations/sticky_activation_manager.h"
#include "components/variations/variations_seed_store.h"
#include "components/version_info/channel.h"
namespace metrics {
class MetricsStateManager;
}
namespace variations {
class EntropyProviders;
// TODO(crbug.com/424154785): Clean this up if low entropy source values are no
// longer transmitted with VariationIDs.
struct CreateTrialsResult {
bool applied_seed = false;
std::optional<bool> seed_has_active_limited_layer;
bool AppliedSeedHasActiveLimitedLayer() const;
};
// Just maps one set of enum values to another. Nothing to see here.
Study::Channel ConvertProductChannelToStudyChannel(
version_info::Channel product_channel);
// Denotes whether Chrome used a variations seed. Also captures (a) the kind of
// seed and (b) the conditions under which the seed was used or failed to be
// used. Exposed for testing.
//
// These values are persisted to logs. Entries should not be renumbered and
// numeric values should never be reused.
enum class SeedUsage {
kRegularSeedUsed = 0,
kExpiredRegularSeedNotUsed = 1,
kUnloadableRegularSeedNotUsed = 2,
kSafeSeedUsed = 3,
kExpiredSafeSeedNotUsed = 4,
// The below three enumerators were deprecated in M100.
// kCorruptedSafeSeedNotUsed = 5,
// kRegularSeedUsedAfterEmptySafeSeedLoaded = 6,
// kExpiredRegularSeedNotUsedAfterEmptySafeSeedLoaded = 7,
// kCorruptedRegularSeedNotUsedAfterEmptySafeSeedLoaded = 8,
kRegularSeedForFutureMilestoneNotUsed = 9,
kSafeSeedForFutureMilestoneNotUsed = 10,
kUnloadableSafeSeedNotUsed = 11,
kNullSeedUsed = 12,
kMaxValue = kNullSeedUsed,
};
// Denotes a variations seed's expiry state. Exposed for testing.
//
// These values are persisted to logs. Entries should not be renumbered and
// numeric values should never be reused.
enum class VariationsSeedExpiry {
kNotExpired = 0,
kFetchTimeMissing = 1,
kExpired = 2,
kMaxValue = kExpired,
};
enum LoadPermanentConsistencyCountryResult {
LOAD_COUNTRY_NO_PREF_NO_SEED = 0,
LOAD_COUNTRY_NO_PREF_HAS_SEED,
LOAD_COUNTRY_INVALID_PREF_NO_SEED,
LOAD_COUNTRY_INVALID_PREF_HAS_SEED,
LOAD_COUNTRY_HAS_PREF_NO_SEED_VERSION_EQ,
LOAD_COUNTRY_HAS_PREF_NO_SEED_VERSION_NEQ,
LOAD_COUNTRY_HAS_BOTH_VERSION_EQ_COUNTRY_EQ,
LOAD_COUNTRY_HAS_BOTH_VERSION_EQ_COUNTRY_NEQ,
LOAD_COUNTRY_HAS_BOTH_VERSION_NEQ_COUNTRY_EQ,
LOAD_COUNTRY_HAS_BOTH_VERSION_NEQ_COUNTRY_NEQ,
LOAD_COUNTRY_HAS_PERMANENT_OVERRIDDEN_COUNTRY,
LOAD_COUNTRY_MAX,
};
class PlatformFieldTrials;
class SafeSeedManagerBase;
class VariationsServiceClient;
// Used to set up field trials based on stored variations seed data.
class VariationsFieldTrialCreator {
public:
// Caller is responsible for ensuring that the VariationsServiceClient
// passed to the constructor stays valid for the lifetime of this object.
//
// |client| provides some platform-specific operations for variations.
// |seed_store| manages seed data.
VariationsFieldTrialCreator(
VariationsServiceClient* client,
std::unique_ptr<VariationsSeedStore> seed_store,
const UIStringOverrider& ui_string_overrider);
VariationsFieldTrialCreator(const VariationsFieldTrialCreator&) =
delete;
VariationsFieldTrialCreator& operator=(
const VariationsFieldTrialCreator&) = delete;
virtual ~VariationsFieldTrialCreator();
// Returns what variations will consider to be the latest country. Returns
// empty if it is not available.
std::string GetLatestCountry() const;
VariationsSeedStore* seed_store() { return seed_store_.get(); }
// Sets up field trials based on stored variations seed data. Returns whether
// setup completed successfully.
//
// |variation_ids| allows for forcing ids selected in chrome://flags.
// |command_line_variation_ids| allows for forcing ids through the
// "--force-variation-ids" command line flag. It should be a comma-separated
// list of variation ids. Ids prefixed with the character "t" will be treated
// as Trigger Variation Ids.
// |extra_overrides| gives a list of feature overrides that should be applied
// after the features explicitly disabled/enabled from the command line via
// --disable-features and --enable-features, but before field trials.
// |feature_list| contains the list of all active features for this client.
// Must not be null.
// |metrics_state_manager| facilitates signaling that Chrome has not yet
// exited cleanly. Must not be null.
// |platform_field_trials| provides the
// platform-specific field trial setup for Chrome. Must not be null.
// |safe_seed_manager| should be notified of the combined server and client
// state that was activated to create the field trials (only when the return
// value is true). Must not be null.
// |add_entropy_source_to_variations_ids| controls if variations ID for the
// low entropy source should be added to FIRST_PARTY variation headers.
// |entropy_providers| Used to provide entropy to field trials.
// TODO(b/263797385): eliminate this argument if we can always add the ID.
//
// NOTE: The ordering of the FeatureList method calls is such that the
// explicit --disable-features and --enable-features from the command line
// take precedence over |extra_overrides|, which takes precedence over the
// field trials.
bool SetUpFieldTrials(
const std::vector<std::string>& variation_ids,
const std::string& command_line_variation_ids,
const std::vector<base::FeatureList::FeatureOverrideInfo>&
extra_overrides,
std::unique_ptr<base::FeatureList> feature_list,
metrics::MetricsStateManager* metrics_state_manager,
PlatformFieldTrials* platform_field_trials,
SafeSeedManagerBase* safe_seed_manager,
bool add_entropy_source_to_variations_ids,
const EntropyProviders& entropy_providers);
// Returns all of the client state used for filtering studies.
// As a side-effect, may update the stored permanent consistency country.
std::unique_ptr<ClientFilterableState> GetClientFilterableStateForVersion(
const base::Version& version);
// Loads the country code to use for filtering permanent consistency studies,
// updating the stored country code if the stored value was for a different
// Chrome version. The country used for permanent consistency studies is kept
// consistent between Chrome upgrades in order to avoid annoying the user due
// to experiment churn while traveling.
std::string LoadPermanentConsistencyCountry(
const base::Version& version,
const std::string& latest_country);
// Returns the country code used for filtering permanent consistency studies.
// This can only be called after field trials have been initialized or if
// prefs::kVariationsPermanentOverriddenCountry has been overridden through
// StoreVariationsOverriddenCountry() in this session.
std::string GetPermanentConsistencyCountry() const;
// Sets the stored permanent country pref for this client.
void StorePermanentCountry(const base::Version& version,
const std::string& country);
// Sets the stored permanent variations overridden country pref for this
// client.
void StoreVariationsOverriddenCountry(const std::string& country);
// Allow the platform that is used to filter the set of active trials to be
// overridden.
void OverrideVariationsPlatform(Study::Platform platform_override);
// Gets the last fetch time of the seed. If using the safe seed, returns
// the safe seed fetch time. Otherwise, returns the last fetch time of the
// latest seed. Returns base::Time() if there is no seed.
base::Time GetSeedFetchTime();
// Returns the client-side time when the seed was last fetched. Returns
// base::Time() if there is no seed.
base::Time GetLatestSeedFetchTime();
// Returns the locale that was used for evaluating trials.
const std::string& application_locale() const { return application_locale_; }
SeedType seed_type() const { return seed_type_; }
// Overrides cached UI strings on the resource bundle once it is initialized.
void OverrideCachedUIStrings();
// Returns whether the map of the cached UI strings to override is empty.
bool IsOverrideResourceMapEmpty();
protected:
// Overrides the string resource specified by |hash| with |str| in the
// resource bundle. Protected for testing.
void OverrideUIString(uint32_t hash, const std::u16string& str);
// Get the platform we're running on, respecting OverrideVariationsPlatform().
// Protected for testing.
Study::Platform GetPlatform();
// Get the client's current form factor. Protected for testing.
Study::FormFactor GetCurrentFormFactor();
#if BUILDFLAG(FIELDTRIAL_TESTING_ENABLED)
// Applies the field trial testing config defined in
// testing/variations/fieldtrial_testing_config.json to the current session.
// Protected and virtual for testing.
virtual void ApplyFieldTrialTestingConfig(base::FeatureList* feature_list);
#endif // BUILDFLAG(FIELDTRIAL_TESTING_ENABLED)
// Read the google group memberships from local-state prefs.
// Protected for testing.
base::flat_set<uint64_t> GetGoogleGroupsFromPrefs();
private:
// Returns true if the loaded VariationsSeed has expired. An expired seed is
// one that (a) was fetched over |kMaxSeedAgeDays| ago and (b) is older than
// the binary build time.
//
// Also, records a couple VariationsSeed-related metrics.
bool HasSeedExpired();
// Returns true if the loaded VariationsSeed is for a future milestone (e.g.
// if the client is on M92 and the seed was fetched with M93). A seed for a
// future milestone is invalid as it may be missing studies filtered out by
// the server.
bool IsSeedForFutureMilestone(bool is_safe_seed);
// Creates field trials from a VariationsSeed. Returns whether the seed was
// successfully applied and whether the seed contains a limited-entropy-mode
// layer.
//
// |entropy_providers| helps to randomize field trial groups.
// |feature_list| associates field trial groups with features.
// |safe_seed_manager| has two responsibilities. It is used to determine which
// seed to apply, if any. If the latest seed is successfully applied, the
// manager also stores the applied variations state.
// |client_state| is used for filtering studies in the seed.
//
// If the seed isn't successfully loaded or if the seed fails some checks
// (e.g. if the seed has expired), then no trials are created from the seed
// and the client uses client-side defaults for features, like
// DISABLED_BY_DEFAULT.
CreateTrialsResult CreateTrialsFromSeed(
const EntropyProviders& entropy_providers,
base::FeatureList* feature_list,
SafeSeedManagerBase* safe_seed_manager,
std::unique_ptr<ClientFilterableState> client_state);
// Reads a seed's data and signature from the file at |json_seed_path| and
// writes them to Local State. Exits Chrome if (A) the file's contents can't
// be loaded or (B) if the contents do not contain |kVariationsCompressedSeed|
// or |kVariationsSeedSignature|. Also forces Chrome to not run in variations
// safe mode. Used for variations seed testing.
void LoadSeedFromJsonFile(const base::FilePath& json_seed_path);
// Returns the seed store. Virtual for testing.
virtual VariationsSeedStore* GetSeedStore();
PrefService* local_state() { return seed_store_->local_state(); }
const PrefService* local_state() const { return seed_store_->local_state(); }
raw_ptr<VariationsServiceClient> client_;
std::unique_ptr<VariationsSeedStore> seed_store_;
// Seed type used for variations.
SeedType seed_type_ = SeedType::kNullSeed;
// Tracks whether |CreateTrialsFromSeed| has been called, to ensure that it is
// called at most once.
bool create_trials_from_seed_called_ = false;
// The application locale won't change after the startup, so we cache the
// value the first time when GetApplicationLocale() is called in the
// constructor.
std::string application_locale_;
// Platform to be used for variations filtering, overriding the current
// platform.
std::optional<Study::Platform> platform_override_;
// Holds the country code to use for filtering permanent consistency studies
std::string permanent_consistency_country_;
// Tracks whether |permanent_consistency_country_| has been initialized to
// ensure it contains a relevant value.
bool permanent_consistency_country_initialized_ = false;
UIStringOverrider ui_string_overrider_;
// Caches the UI strings which need to be overridden in the resource bundle.
// These strings are cached before the resource bundle is initialized.
std::unordered_map<int, std::u16string> overridden_strings_map_;
StickyActivationManager sticky_activation_manager_;
SEQUENCE_CHECKER(sequence_checker_);
};
// A testing feature that forces a crash during field trial creation
// on developer and test builds.
BASE_DECLARE_FEATURE(kForceFieldTrialSetupCrashForTesting);
} // namespace variations
#endif // COMPONENTS_VARIATIONS_SERVICE_VARIATIONS_FIELD_TRIAL_CREATOR_H_