File: first_run_service_dice_statics.cc

package info (click to toggle)
chromium 120.0.6099.224-1~deb11u1
  • links: PTS, VCS
  • area: main
  • in suites: bullseye
  • size: 6,112,112 kB
  • sloc: cpp: 32,907,025; ansic: 8,148,123; javascript: 3,679,536; python: 2,031,248; asm: 959,718; java: 804,675; xml: 617,256; sh: 111,417; objc: 100,835; perl: 88,443; cs: 53,032; makefile: 29,579; fortran: 24,137; php: 21,162; tcl: 21,147; sql: 20,809; ruby: 17,735; pascal: 12,864; yacc: 8,045; lisp: 3,388; lex: 1,323; ada: 727; awk: 329; jsp: 267; csh: 117; exp: 43; sed: 37
file content (201 lines) | stat: -rw-r--r-- 7,499 bytes parent folder | download
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
// 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.

#include "chrome/browser/ui/startup/first_run_service.h"

#include <memory>
#include <utility>

#include "base/feature_list.h"
#include "base/logging.h"
#include "base/metrics/field_trial.h"
#include "base/metrics/field_trial_params.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/first_run/first_run.h"
#include "chrome/browser/metrics/chrome_metrics_service_accessor.h"
#include "chrome/browser/signin/signin_features.h"
#include "chrome/common/channel_info.h"
#include "chrome/common/pref_names.h"
#include "components/prefs/pref_service.h"
#include "components/variations/variations_associated_data.h"
#include "components/version_info/channel.h"

#if !BUILDFLAG(ENABLE_DICE_SUPPORT)
#error "Unsupported platform"
#endif

namespace {
// The field trial name.
const char kTrialName[] = "ForYouFreStudy";

// Group names for the trial.
const char kEnabledGroup[] = "ClientSideEnabled-2";
const char kDisabledGroup[] = "ClientSideDisabled-2";
const char kDefaultGroup[] = "Default";

// Probabilities for all field trial groups add up to kTotalProbability.
const base::FieldTrial::Probability kTotalProbability = 100;

std::string PickTrialGroupWithoutActivation(base::FieldTrial& trial,
                                            version_info::Channel channel) {
  int enabled_percent;
  int disabled_percent;
  int default_percent;
  switch (channel) {
    case version_info::Channel::UNKNOWN:
    case version_info::Channel::CANARY:
    case version_info::Channel::DEV:
    case version_info::Channel::BETA:
      enabled_percent = 50;
      disabled_percent = 50;
      default_percent = 0;
      break;
    case version_info::Channel::STABLE:
      enabled_percent = 1;
      disabled_percent = 1;
      default_percent = 98;
      break;
  }
  DCHECK_EQ(kTotalProbability,
            enabled_percent + disabled_percent + default_percent);

  trial.AppendGroup(kEnabledGroup, enabled_percent);
  trial.AppendGroup(kDisabledGroup, disabled_percent);
  trial.AppendGroup(kDefaultGroup, default_percent);

  return trial.GetGroupNameWithoutActivation();
}

}  // namespace

// static
void FirstRunService::SetUpClientSideFieldTrialIfNeeded(
    const base::FieldTrial::EntropyProvider& entropy_provider,
    base::FeatureList* feature_list) {
  if (!first_run::IsChromeFirstRun()) {
    // We should not check these features outside of the first run.
    DVLOG(1) << "Not setting up client-side trial for the FRE, this is not the "
                "first run.";
    return;
  }

  // Make sure that Finch, fieldtrial_testing_config and command line flags take
  // precedence over features defined here. In particular, not detecting
  // fieldtrial_testing_config triggers a DCHECK.

  if (base::FieldTrialList::Find(kTrialName)) {
    DVLOG(1) << "Not setting up client-side trial for the FRE, trial "
                "already registered";
    return;
  }

  bool is_behaviour_feature_associated =
      feature_list->HasAssociatedFieldTrialByFeatureName(kForYouFre.name);
  bool is_measurement_feature_associated =
      feature_list->HasAssociatedFieldTrialByFeatureName(kForYouFre.name);
  if (is_behaviour_feature_associated || is_measurement_feature_associated) {
    LOG(WARNING) << "Not setting up client-side trial for the FRE, feature(s) "
                    "already overridden:"
                 << (is_behaviour_feature_associated ? " ForYouFre" : "")
                 << (is_measurement_feature_associated
                         ? " ForYouFreSyntheticTrialRegistration"
                         : "");
    return;
  }

  // Proceed with actually setting up the field trial.
  SetUpClientSideFieldTrial(entropy_provider, feature_list,
                            chrome::GetChannel());
}

// static
void FirstRunService::SetUpClientSideFieldTrial(
    const base::FieldTrial::EntropyProvider& entropy_provider,
    base::FeatureList* feature_list,
    version_info::Channel channel) {
  // Set up the trial and determine the group for the current client.
  scoped_refptr<base::FieldTrial> trial =
      base::FieldTrialList::FactoryGetFieldTrial(
          kTrialName, kTotalProbability, kDefaultGroup, entropy_provider);
  std::string group_name = PickTrialGroupWithoutActivation(*trial, channel);

  // Set up the state of the features based on the obtained group.
  base::FeatureList::OverrideState behaviour_feature_state =
      group_name == kEnabledGroup ? base::FeatureList::OVERRIDE_ENABLE_FEATURE
                                  : base::FeatureList::OVERRIDE_DISABLE_FEATURE;
  base::FeatureList::OverrideState measurement_feature_state =
      group_name != kDefaultGroup ? base::FeatureList::OVERRIDE_ENABLE_FEATURE
                                  : base::FeatureList::OVERRIDE_DISABLE_FEATURE;

  if (measurement_feature_state == base::FeatureList::OVERRIDE_ENABLE_FEATURE) {
    base::AssociateFieldTrialParams(kTrialName, group_name,
                                    {{kForYouFreStudyGroup.name, group_name}});
  }

  feature_list->RegisterFieldTrialOverride(
      kForYouFre.name, behaviour_feature_state, trial.get());
  feature_list->RegisterFieldTrialOverride(
      kForYouFreSyntheticTrialRegistration.name, measurement_feature_state,
      trial.get());

  // Activate only after the overrides are completed.
  trial->Activate();
}

// static
void FirstRunService::EnsureStickToFirstRunCohort() {
  PrefService* local_state = g_browser_process->local_state();
  if (!local_state) {
    return;  // Can be null in unit tests;
  }

  if (!local_state->GetBoolean(prefs::kFirstRunFinished)) {
    // This user did not see the FRE. Their first run either happened before the
    // feature was enabled, or it's happening right now. In the former case we
    // don't enroll them, and in the latter, they will be enrolled right before
    // starting the FRE.
    return;
  }

  auto enrolled_study_group =
      local_state->GetString(prefs::kFirstRunStudyGroup);
  if (enrolled_study_group.empty()) {
    // The user was not enrolled or exited the study at some point.
    return;
  }

  RegisterSyntheticFieldTrial(enrolled_study_group);
}

// static
void FirstRunService::JoinFirstRunCohort() {
  PrefService* local_state = g_browser_process->local_state();
  if (!local_state) {
    return;  // Can be null in unit tests;
  }

  // The First Run experience depends on experiment groups (see params
  // associated with the `kForYouFre` feature). To measure the long terms impact
  // of this one-shot experience, we save an associated group name to prefs so
  // we can report it as a synthetic trial for understanding the effects for
  // each specific configuration, disambiguating it from other clients who had
  // a different experience (or did not see the FRE for some reason).
  std::string active_study_group = kForYouFreStudyGroup.Get();
  if (active_study_group.empty()) {
    return;  // No active study, no need to sign up.
  }

  local_state->SetString(prefs::kFirstRunStudyGroup, active_study_group);
  RegisterSyntheticFieldTrial(active_study_group);
}

// static
void FirstRunService::RegisterSyntheticFieldTrial(
    const std::string& group_name) {
  DCHECK(!group_name.empty());

  ChromeMetricsServiceAccessor::RegisterSyntheticFieldTrial(
      FirstRunService::kSyntheticTrialName, group_name,
      variations::SyntheticTrialAnnotationMode::kCurrentLog);
}