123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184 |
- // Copyright 2017 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/synthetic_trial_registry.h"
- #include <algorithm>
- #include "base/containers/cxx20_erase.h"
- #include "base/metrics/histogram_functions.h"
- #include "base/observer_list.h"
- #include "base/strings/string_number_conversions.h"
- #include "components/variations/active_field_trials.h"
- #include "components/variations/hashing.h"
- #include "components/variations/variations_associated_data.h"
- namespace variations {
- namespace internal {
- const base::Feature kExternalExperimentAllowlist{
- "ExternalExperimentAllowlist", base::FEATURE_ENABLED_BY_DEFAULT};
- } // namespace internal
- SyntheticTrialRegistry::SyntheticTrialRegistry(
- bool enable_external_experiment_allowlist)
- : enable_external_experiment_allowlist_(
- enable_external_experiment_allowlist &&
- base::FeatureList::IsEnabled(
- internal::kExternalExperimentAllowlist)) {}
- SyntheticTrialRegistry::SyntheticTrialRegistry()
- : enable_external_experiment_allowlist_(base::FeatureList::IsEnabled(
- internal::kExternalExperimentAllowlist)) {}
- SyntheticTrialRegistry::~SyntheticTrialRegistry() = default;
- void SyntheticTrialRegistry::AddSyntheticTrialObserver(
- SyntheticTrialObserver* observer) {
- synthetic_trial_observer_list_.AddObserver(observer);
- if (!synthetic_trial_groups_.empty())
- observer->OnSyntheticTrialsChanged(synthetic_trial_groups_);
- }
- void SyntheticTrialRegistry::RemoveSyntheticTrialObserver(
- SyntheticTrialObserver* observer) {
- synthetic_trial_observer_list_.RemoveObserver(observer);
- }
- void SyntheticTrialRegistry::RegisterExternalExperiments(
- const std::string& fallback_study_name,
- const std::vector<int>& experiment_ids,
- SyntheticTrialRegistry::OverrideMode mode) {
- DCHECK(!fallback_study_name.empty());
- base::FieldTrialParams params;
- if (enable_external_experiment_allowlist_ &&
- !GetFieldTrialParamsByFeature(internal::kExternalExperimentAllowlist,
- ¶ms)) {
- return;
- }
- // When overriding previous external experiments, remove them now.
- if (mode == kOverrideExistingIds) {
- auto is_external = [](const SyntheticTrialGroup& group) {
- return group.is_external();
- };
- base::EraseIf(synthetic_trial_groups_, is_external);
- }
- const base::TimeTicks start_time = base::TimeTicks::Now();
- int trials_added = 0;
- for (int experiment_id : experiment_ids) {
- const std::string experiment_id_str = base::NumberToString(experiment_id);
- const base::StringPiece study_name =
- GetStudyNameForExpId(fallback_study_name, params, experiment_id_str);
- if (study_name.empty())
- continue;
- const uint32_t trial_hash = HashName(study_name);
- // If existing ids shouldn't be overridden, skip entries whose study names
- // are already registered.
- if (mode == kDoNotOverrideExistingIds) {
- auto matches_trial = [trial_hash](const SyntheticTrialGroup& group) {
- return group.id().name == trial_hash;
- };
- const auto& groups = synthetic_trial_groups_;
- if (std::any_of(groups.begin(), groups.end(), matches_trial)) {
- continue;
- }
- }
- const uint32_t group_hash = HashName(experiment_id_str);
- // Since external experiments are not based on Chrome's low entropy source,
- // they are only sent to Google web properties for signed-in users to make
- // sure they couldn't be used to identify a user that's not signed-in.
- AssociateGoogleVariationIDForceHashes(
- GOOGLE_WEB_PROPERTIES_SIGNED_IN, {trial_hash, group_hash},
- static_cast<VariationID>(experiment_id));
- SyntheticTrialGroup entry(
- study_name, experiment_id_str,
- variations::SyntheticTrialAnnotationMode::kNextLog);
- entry.SetStartTime(start_time);
- entry.SetIsExternal(true);
- synthetic_trial_groups_.push_back(entry);
- trials_added++;
- }
- base::UmaHistogramCounts100("UMA.ExternalExperiment.GroupCount",
- trials_added);
- if (trials_added > 0)
- NotifySyntheticTrialObservers();
- }
- void SyntheticTrialRegistry::RegisterSyntheticFieldTrial(
- const SyntheticTrialGroup& trial) {
- for (auto& entry : synthetic_trial_groups_) {
- if (entry.id().name == trial.id().name) {
- // Don't necessarily need to notify observers when setting
- // |annotation_mode| as it is only used when producing metrics reports
- // and does not affect variations service.
- entry.SetAnnotationMode(trial.annotation_mode());
- if (entry.id().group != trial.id().group) {
- entry.SetGroupName(trial.group_name());
- entry.SetStartTime(base::TimeTicks::Now());
- NotifySyntheticTrialObservers();
- }
- return;
- }
- }
- SyntheticTrialGroup trial_group = trial;
- trial_group.SetStartTime(base::TimeTicks::Now());
- synthetic_trial_groups_.push_back(trial_group);
- NotifySyntheticTrialObservers();
- }
- base::StringPiece SyntheticTrialRegistry::GetStudyNameForExpId(
- const std::string& fallback_study_name,
- const base::FieldTrialParams& params,
- const std::string& experiment_id) {
- if (!enable_external_experiment_allowlist_)
- return fallback_study_name;
- const auto it = params.find(experiment_id);
- if (it == params.end())
- return base::StringPiece();
- // To support additional parameters being passed, besides the study name,
- // truncate the study name at the first ',' character.
- // For example, for an entry like {"1234": "StudyName,FOO"}, we only want the
- // "StudyName" part. This allows adding support for additional things like FOO
- // in the future without backwards compatibility problems.
- const size_t comma_pos = it->second.find(',');
- const size_t truncate_pos =
- (comma_pos == std::string::npos ? it->second.length() : comma_pos);
- return base::StringPiece(it->second.data(), truncate_pos);
- }
- void SyntheticTrialRegistry::NotifySyntheticTrialObservers() {
- for (SyntheticTrialObserver& observer : synthetic_trial_observer_list_) {
- observer.OnSyntheticTrialsChanged(synthetic_trial_groups_);
- }
- }
- void SyntheticTrialRegistry::GetSyntheticFieldTrialsOlderThan(
- base::TimeTicks time,
- std::vector<ActiveGroupId>* synthetic_trials,
- base::StringPiece suffix) const {
- DCHECK(synthetic_trials);
- synthetic_trials->clear();
- base::FieldTrial::ActiveGroups active_groups;
- for (const auto& entry : synthetic_trial_groups_) {
- if (entry.start_time() <= time ||
- entry.annotation_mode() == SyntheticTrialAnnotationMode::kCurrentLog)
- active_groups.push_back(entry.active_group());
- }
- GetFieldTrialActiveGroupIdsForActiveGroups(suffix, active_groups,
- synthetic_trials);
- }
- } // namespace variations
|