summaryrefslogtreecommitdiffstats
path: root/chromium/components/variations/variations_layers.cc
blob: b4f07e1370056c33470f80bab77790980dc9dd43 (plain)
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
// Copyright 2020 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/variations_layers.h"

#include "base/metrics/field_trial.h"

namespace variations {

namespace {

// Returns a double in the [0, 1] range to be used to select a slot within
// a layer with the given randomization seed value.
double GetEntropyForLayer(
    const base::FieldTrial::EntropyProvider* entropy_provider,
    uint32_t randomization_seed) {
  // GetEntropyForTrial will ignore the trial_name parameter in favor of the
  // randomization_seed.
  return entropy_provider->GetEntropyForTrial(/*trial_name=*/"",
                                              randomization_seed);
}

// Iterates through the members of the given layer proto definition, and
// returns the ID of the member which contains that slot (if any).
absl::optional<uint32_t> FindActiveMemberBySlot(uint32_t chosen_slot,
                                                const Layer& layer_proto) {
  for (const Layer::LayerMember& member : layer_proto.members()) {
    if (!member.id())
      continue;

    for (const Layer::LayerMember::SlotRange& slot : member.slots()) {
      if (slot.start() <= chosen_slot && chosen_slot <= slot.end())
        return member.id();
    }
  }

  return absl::nullopt;
}

}  // namespace

VariationsLayers::LayerInfo::LayerInfo(
    absl::optional<uint32_t> active_member_id,
    Layer::EntropyMode entropy_mode)
    : active_member_id(active_member_id), entropy_mode(entropy_mode) {}

VariationsLayers::LayerInfo::~LayerInfo() = default;

VariationsLayers::LayerInfo::LayerInfo(const LayerInfo& other) {
  active_member_id = other.active_member_id;
  entropy_mode = other.entropy_mode;
}

VariationsLayers::VariationsLayers(
    const VariationsSeed& seed,
    const base::FieldTrial::EntropyProvider* low_entropy_provider) {
  if (!low_entropy_provider) {
    // Android WebView does not support low-entropy field trials.
    return;
  }

  // TODO(crbug.com/1154033): Support a way to expire old/unused layers so they
  // no longer get processed by the clients.
  for (const Layer& layer_proto : seed.layers())
    ConstructLayer(*low_entropy_provider, layer_proto);
}

VariationsLayers::VariationsLayers() = default;

VariationsLayers::~VariationsLayers() = default;

void VariationsLayers::ConstructLayer(
    const base::FieldTrial::EntropyProvider& low_entropy_provider,
    const Layer& layer_proto) {
  if (layer_proto.id() == 0 || layer_proto.num_slots() == 0 ||
      layer_proto.members_size() == 0) {
    return;
  }

  double entropy_value;
  if (layer_proto.entropy_mode() == Layer::LOW) {
    entropy_value =
        GetEntropyForLayer(&low_entropy_provider, layer_proto.salt());
  } else {
    const base::FieldTrial::EntropyProvider* default_entropy_provider =
        base::FieldTrialList::GetEntropyProviderForOneTimeRandomization();
    CHECK(default_entropy_provider);
    entropy_value =
        GetEntropyForLayer(default_entropy_provider, layer_proto.salt());
  }

  const double kEpsilon = 1e-8;
  // Add a tiny epsilon to get consistent values when converting the double
  // to the integer slots; see comment in
  // base::FieldTrialList::GetGroupBoundaryValue() for more details.
  uint32_t chosen_slot = std::min(
      static_cast<uint32_t>(layer_proto.num_slots() * entropy_value + kEpsilon),
      layer_proto.num_slots() - 1);

  active_member_for_layer_.emplace(
      layer_proto.id(),
      LayerInfo{FindActiveMemberBySlot(chosen_slot, layer_proto),
                layer_proto.entropy_mode()});
}

bool VariationsLayers::IsLayerMemberActive(uint32_t layer_id,
                                           uint32_t member_id) const {
  auto layer_iter = active_member_for_layer_.find(layer_id);
  if (layer_iter == active_member_for_layer_.end())
    return false;

  return layer_iter->second.active_member_id &&
         (member_id == layer_iter->second.active_member_id.value());
}

bool VariationsLayers::IsLayerUsingDefaultEntropy(uint32_t layer_id) const {
  auto layer_iter = active_member_for_layer_.find(layer_id);
  if (layer_iter == active_member_for_layer_.end())
    return false;

  return layer_iter->second.entropy_mode == Layer::DEFAULT;
}

}  // namespace variations