File: variations_field_trial_creator_base.h

package info (click to toggle)
chromium 139.0.7258.127-1
  • links: PTS, VCS
  • area: main
  • in suites:
  • size: 6,122,068 kB
  • sloc: cpp: 35,100,771; ansic: 7,163,530; javascript: 4,103,002; python: 1,436,920; asm: 946,517; xml: 746,709; pascal: 187,653; perl: 88,691; sh: 88,436; objc: 79,953; sql: 51,488; cs: 44,583; fortran: 24,137; makefile: 22,147; tcl: 15,277; php: 13,980; yacc: 8,984; ruby: 7,485; awk: 3,720; lisp: 3,096; lex: 1,327; ada: 727; jsp: 228; sed: 36
file content (352 lines) | stat: -rw-r--r-- 14,992 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
// Copyright 2023 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_BASE_H_
#define COMPONENTS_VARIATIONS_SERVICE_VARIATIONS_FIELD_TRIAL_CREATOR_BASE_H_

// This file contains a base class for VariationsFieldTrialCreator. Its primary
// goal is to minimize generated code size, even at the expense of
// functionality, and to be usable in ChromeOS early boot as part of a
// standalone executable.
// In particular, it will *not*:
// 1) determine locale (this requires linking in libicu, which is several
//    hundred KiB). Instead, it will accept locale as a parameter to the
//    constructor.
// 2) modify cached UI strings (we remove the ui::ResourceBundle dep entirely)
//
// All such functionality must be implemented by subclasses (e.g.
// VariationsFieldTrialCreator)

#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/variations_service_client.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_limited_layer;
};

// 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 VariationsFieldTrialCreatorBase {
 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.
  // |locale_cb| computes the locale, given a PrefService for local_state.
  VariationsFieldTrialCreatorBase(
      VariationsServiceClient* client,
      std::unique_ptr<VariationsSeedStore> seed_store,
      base::OnceCallback<std::string(PrefService*)> locale_cb);

  VariationsFieldTrialCreatorBase(const VariationsFieldTrialCreatorBase&) =
      delete;
  VariationsFieldTrialCreatorBase& operator=(
      const VariationsFieldTrialCreatorBase&) = delete;

  virtual ~VariationsFieldTrialCreatorBase();

  // 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 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.
  // To be implemented by subclasses, if they have need for UI strings.
  virtual void OverrideCachedUIStrings() = 0;

  // Returns whether the map of the cached UI strings to override is empty.
  // To be implemented by subclasses, if they have need for UI strings.
  virtual bool IsOverrideResourceMapEmpty() = 0;

  // Returns the client-side time when the seed was last fetched. Returns
  // base::Time() if there is no seed.
  base::Time GetLatestSeedFetchTime();

 protected:
  // 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();

  // Overrides the string resource specified by |hash| with |str| in the
  // resource bundle. Protected for testing.
  // To be implemented by subclasses if they need UI string overrides.
  virtual void OverrideUIString(uint32_t hash, const std::u16string& str) = 0;

 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_;

  // 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_;

  // 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;

  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_BASE_H_