Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(36)

Side by Side Diff: chromecast/base/cast_features.cc

Issue 2825873002: [Chromecast] Use base::FeatureList to control features. (Closed)
Patch Set: gn check Created 3 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2017 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "chromecast/base/cast_features.h"
6
7 #include "base/command_line.h"
8 #include "base/feature_list.h"
9 #include "base/lazy_instance.h"
10 #include "base/metrics/field_trial.h"
11 #include "base/metrics/field_trial_param_associator.h"
12 #include "base/metrics/field_trial_params.h"
13 #include "base/strings/string_number_conversions.h"
14 #include "base/values.h"
15
16 namespace chromecast {
17 namespace {
18
19 // A constant used to always activate a FieldTrial.
20 const base::FieldTrial::Probability k100PercentProbability = 100;
21
22 // The name of the default group to use for Cast DCS features.
23 const char kDefaultDCSFeaturesGroup[] = "default_dcs_features_group";
24
25 base::LazyInstance<std::unordered_set<int32_t>>::Leaky g_experiment_ids =
26 LAZY_INSTANCE_INITIALIZER;
27 bool g_experiment_ids_initialized = false;
28
29 void SetExperimentIds(const base::ListValue& list) {
30 DCHECK(!g_experiment_ids_initialized);
31 int32_t id;
Alexei Svitkine (slow) 2017/04/21 19:48:01 Nit: Move this into the loop right before it's use
slan 2017/04/21 21:43:27 Done.
32 auto ids = base::MakeUnique<std::unordered_set<int32_t>>();
Alexei Svitkine (slow) 2017/04/21 19:48:01 I don't think you need to allocate this on the hea
slan 2017/04/21 21:43:27 Right you are, this was a relic of an older patch.
33 for (size_t i = 0; i < list.GetSize(); ++i) {
34 if (list.GetInteger(i, &id)) {
35 ids->insert(id);
36 } else {
37 LOG(ERROR) << "Non-integer value found in experiment id list!";
38 }
39 }
40 g_experiment_ids.Get().swap(*ids);
41 g_experiment_ids_initialized = true;
42 }
43 } // namespace
44
45 // An iterator for a base::DictionaryValue. Use an alias for brevity in loops.
46 using Iterator = base::DictionaryValue::Iterator;
47
48 void InitializeFeatureList(const base::DictionaryValue& dcs_features,
49 const base::ListValue& dcs_experiment_ids,
50 const std::string& cmd_line_enable_features,
51 const std::string& cmd_line_disable_features) {
52 DCHECK(!base::FeatureList::GetInstance());
53
54 // Set the experiments.
55 SetExperimentIds(dcs_experiment_ids);
56
57 // Initialize the FeatureList from the command line.
58 auto feature_list = base::MakeUnique<base::FeatureList>();
59 feature_list->InitializeFromCommandLine(cmd_line_enable_features,
60 cmd_line_disable_features);
61
62 // Override defaults from the DCS config.
63 for (Iterator it(dcs_features); !it.IsAtEnd(); it.Advance()) {
64 // Each feature must have its own FieldTrial object. Since experiments are
65 // controlled server-side for Chromecast, and this class is designed with a
66 // client-side experimentation framework in mind, these parameters are
67 // carefully chosen:
68 // - The field trial name is unused for our purposes, we simple need it to
69 // be unique among features.
Alexei Svitkine (slow) 2017/04/21 19:48:01 Can this sentence be expanded to mention what's be
slan 2017/04/21 21:43:27 Sure, let me know if it's clearer now.
70 // - The probability is hard-coded to 100% so that the FeatureList always
71 // respects the value from DCS.
72 // - The default group is unused; it will be the same for every feature.
73 // - Expiration year, month, and day use a special value such that the
74 // feature will never expire.
75 // - SESSION_RANDOMIZED is used to prevent the need for an
76 // entropy_provider. However, this value doesn't matter.
77 // - We don't care about the group_id.
78 //
79 const std::string& field_trial_name = it.key();
Alexei Svitkine (slow) 2017/04/21 19:48:01 Nit: I'd actually name this feature_name and also
slan 2017/04/21 21:43:28 I like it. Done.
80 auto* field_trial = base::FieldTrialList::FactoryGetFieldTrial(
81 field_trial_name, k100PercentProbability, kDefaultDCSFeaturesGroup,
82 base::FieldTrialList::kNoExpirationYear, 1 /* month */, 1 /* day */,
83 base::FieldTrial::SESSION_RANDOMIZED, nullptr);
84
85 bool enabled;
86 if (it.value().GetAsBoolean(&enabled)) {
87 // A boolean entry simply either enables or disables a feature.
88 feature_list->RegisterFieldTrialOverride(
89 it.key(),
90 enabled ? base::FeatureList::OVERRIDE_ENABLE_FEATURE
91 : base::FeatureList::OVERRIDE_DISABLE_FEATURE,
92 field_trial);
93 continue;
94 }
95
96 const base::DictionaryValue* params_dict;
97 if (it.value().GetAsDictionary(&params_dict)) {
98 // A dictionary entry implies that the feature is enabled.
99 feature_list->RegisterFieldTrialOverride(
100 it.key(), base::FeatureList::OVERRIDE_ENABLE_FEATURE, field_trial);
101
102 // If the feature has not been overriden from the command line, set its
103 // parameters accordingly.
104 if (!feature_list->IsFeatureOverriddenFromCommandLine(
105 it.key(), base::FeatureList::OVERRIDE_DISABLE_FEATURE)) {
106 // Build a map of the FieldTrial parameters and associate it to the
107 // FieldTrial.
108 base::FieldTrialParamAssociator::FieldTrialParams params;
109 for (Iterator p(*params_dict); !p.IsAtEnd(); p.Advance()) {
110 std::string val;
111 if (p.value().GetAsString(&val)) {
112 params[p.key()] = val;
113 } else {
114 LOG(ERROR) << "Entry in params dict for \"" << it.key() << "\""
115 << " feature is not a string. Skipping.";
116 }
117 }
118
119 // Register the params, so that they can be queried by client code.
120 bool success = base::AssociateFieldTrialParams(
121 field_trial_name, kDefaultDCSFeaturesGroup, params);
122 DCHECK(success);
123 }
124 continue;
125 }
126
127 // Other base::Value types are not supported.
128 LOG(ERROR) << "A DCS feature mapped to an unsupported value. key: "
129 << it.key() << " type: " << it.value().type();
130 }
131
132 base::FeatureList::SetInstance(std::move(feature_list));
133 }
134
135 std::unique_ptr<base::DictionaryValue> GetOverriddenFeaturesForStorage(
Alexei Svitkine (slow) 2017/04/21 19:48:01 Nit: base::DictionaryValue is already copiable, so
136 const base::DictionaryValue& features) {
137 auto persistent_dict = base::MakeUnique<base::DictionaryValue>();
138
139 // |features| maps feature names to either a boolean or a dict of params.
140 for (Iterator it(features); !it.IsAtEnd(); it.Advance()) {
141 bool enabled;
142 if (it.value().GetAsBoolean(&enabled)) {
143 persistent_dict->SetBoolean(it.key(), enabled);
144 continue;
145 }
146
147 const base::DictionaryValue* params_dict;
148 if (it.value().GetAsDictionary(&params_dict)) {
149 auto params = base::MakeUnique<base::DictionaryValue>();
150
151 bool bval;
152 int ival;
153 double dval;
154 std::string sval;
155 for (Iterator p(*params_dict); !p.IsAtEnd(); p.Advance()) {
156 const auto& param_key = p.key();
157 const auto& param_val = p.value();
158 if (param_val.GetAsBoolean(&bval)) {
159 params->SetString(param_key, bval ? "true" : "false");
160 } else if (param_val.GetAsInteger(&ival)) {
161 params->SetString(param_key, base::IntToString(ival));
162 } else if (param_val.GetAsDouble(&dval)) {
163 params->SetString(param_key, base::DoubleToString(dval));
164 } else if (param_val.GetAsString(&sval)) {
165 params->SetString(param_key, sval);
166 } else {
167 LOG(ERROR) << "Entry in params dict for \"" << it.key() << "\""
168 << " is not of a supported type (key: " << p.key()
169 << ", type: " << param_val.type();
170 }
171 }
172 persistent_dict->Set(it.key(), std::move(params));
173 continue;
174 }
175
176 // Other base::Value types are not supported.
177 LOG(ERROR) << "A DCS feature mapped to an unsupported value. key: "
178 << it.key() << " type: " << it.value().type();
179 }
180
181 return persistent_dict;
182 }
183
184 const std::unordered_set<int32_t>& GetDCSExperimentIds() {
185 DCHECK(g_experiment_ids_initialized);
186 return g_experiment_ids.Get();
187 }
188
189 void ResetCastFeaturesForTest() {
Alexei Svitkine (slow) 2017/04/21 19:48:01 Nit: I think convention is ForTesting rather than
slan 2017/04/21 21:43:27 Done.
190 g_experiment_ids_initialized = false;
191 base::FeatureList::ClearInstanceForTesting();
192 }
193
194 } // namespace chromecast
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698