blob: e5b2db5fdb673f37606cc80c143edc2934482245 [file] [log] [blame]
// Copyright 2021 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/privacy_budget/identifiability_study_group_settings.h"
#include <algorithm>
#include <numeric>
#include "base/containers/flat_map.h"
#include "base/metrics/histogram_functions.h"
#include "base/ranges/algorithm.h"
#include "chrome/common/privacy_budget/privacy_budget_features.h"
#include "chrome/common/privacy_budget/types.h"
#include "third_party/blink/public/common/privacy_budget/identifiable_surface.h"
namespace {
void UmaHistogramFinchConfigValidation(bool valid) {
base::UmaHistogramBoolean(
"PrivacyBudget.Identifiability.FinchConfigValidationResult", valid);
}
} // namespace
// static
IdentifiabilityStudyGroupSettings
IdentifiabilityStudyGroupSettings::InitFromFeatureParams() {
return InitFrom(
base::FeatureList::IsEnabled(features::kIdentifiabilityStudy),
features::kIdentifiabilityStudyExpectedSurfaceCount.Get(),
features::kIdentifiabilityStudyActiveSurfaceBudget.Get(),
features::kIdentifiabilityStudyBlocks.Get(),
features::kIdentifiabilityStudyBlockWeights.Get(),
features::kIdentifiabilityStudyAllowedRandomTypes.Get(),
features::kIdentifiabilityStudyReidSurfaceBlocks.Get(),
features::kIdentifiabilityStudyReidSurfaceBlocksSaltsRanges.Get(),
features::kIdentifiabilityStudyReidSurfaceBlocksBits.Get(),
features::kIdentifiabilityStudyReidBlocksNoiseProbabilities.Get());
}
// static
IdentifiabilityStudyGroupSettings IdentifiabilityStudyGroupSettings::InitFrom(
bool enabled,
int expected_surface_count,
int surface_budget,
const std::string& blocks,
const std::string& blocks_weights,
const std::string& allowed_random_types,
const std::string& reid_blocks,
const std::string& reid_blocks_salts_ranges,
const std::string& reid_blocks_bits,
const std::string& reid_blocks_noise_probabilities) {
return IdentifiabilityStudyGroupSettings(
enabled, expected_surface_count, surface_budget,
DecodeIdentifiabilityFieldTrialParam<IdentifiableSurfaceBlocks>(blocks),
DecodeIdentifiabilityFieldTrialParam<std::vector<double>>(blocks_weights),
DecodeIdentifiabilityFieldTrialParam<
std::vector<blink::IdentifiableSurface::Type>>(allowed_random_types),
DecodeIdentifiabilityFieldTrialParam<IdentifiableSurfaceBlocks>(
reid_blocks),
DecodeIdentifiabilityFieldTrialParam<std::vector<uint64_t>>(
reid_blocks_salts_ranges),
DecodeIdentifiabilityFieldTrialParam<std::vector<int>>(reid_blocks_bits),
DecodeIdentifiabilityFieldTrialParam<std::vector<double>>(
reid_blocks_noise_probabilities));
}
IdentifiabilityStudyGroupSettings::IdentifiabilityStudyGroupSettings(
bool enabled,
int expected_surface_count,
int surface_budget,
IdentifiableSurfaceBlocks blocks,
std::vector<double> blocks_weights,
std::vector<blink::IdentifiableSurface::Type> allowed_random_types,
IdentifiableSurfaceBlocks reid_blocks,
std::vector<uint64_t> reid_blocks_salts_ranges,
std::vector<int> reid_blocks_bits,
std::vector<double> reid_blocks_noise_probabilities)
: enabled_(enabled),
expected_surface_count_(std::clamp<int>(
expected_surface_count,
0,
features::kMaxIdentifiabilityStudyExpectedSurfaceCount)),
surface_budget_(std::clamp<int>(
surface_budget,
0,
features::kMaxIdentifiabilityStudyActiveSurfaceBudget)),
blocks_(std::move(blocks)),
blocks_weights_(std::move(blocks_weights)),
reid_blocks_(std::move(reid_blocks)),
reid_blocks_salts_ranges_(std::move(reid_blocks_salts_ranges)),
reid_blocks_bits_(std::move(reid_blocks_bits)),
reid_blocks_noise_probabilities_(
std::move(reid_blocks_noise_probabilities)),
allowed_random_types_(std::move(allowed_random_types)) {
bool validates = Validate();
UmaHistogramFinchConfigValidation(validates);
if (!validates)
enabled_ = false;
}
IdentifiabilityStudyGroupSettings::~IdentifiabilityStudyGroupSettings() =
default;
IdentifiabilityStudyGroupSettings::IdentifiabilityStudyGroupSettings(
IdentifiabilityStudyGroupSettings&&) = default;
bool IdentifiabilityStudyGroupSettings::Validate() {
// Disabling the Identifiability Study feature flag is a valid configuration.
if (!enabled_)
return true;
// If the study is enabled, at least one of assigned-block-sampling or
// reid-score-estimation or random-surface-assignment should be enabled.
if (!IsUsingAssignedBlockSampling() && !IsUsingReidScoreEstimator() &&
!IsUsingRandomSampling()) {
return false;
}
if (IsUsingAssignedBlockSampling() && IsUsingRandomSampling())
return false;
if (IsUsingAssignedBlockSampling() && !ValidateAssignedBlockSampling())
return false;
if (IsUsingReidScoreEstimator() && !ValidateReidBlockEstimator())
return false;
return true;
}
bool IdentifiabilityStudyGroupSettings::ValidateAssignedBlockSampling() {
// For every block there should be a weight.
if (blocks_weights_.size() != blocks_.size())
return false;
// Weights should be positive.
for (double weight : blocks_weights_) {
if (weight < 0)
return false;
}
// Each single surface should have probability lower than the threshold to be
// selected.
base::flat_map<blink::IdentifiableSurface, double> weight_per_surface;
for (size_t i = 0; i < blocks_.size(); ++i) {
for (const blink::IdentifiableSurface& surface : blocks_[i]) {
auto el = weight_per_surface.find(surface);
if (el != weight_per_surface.end()) {
weight_per_surface.insert_or_assign(surface,
el->second + blocks_weights_[i]);
} else {
weight_per_surface.insert_or_assign(surface, blocks_weights_[i]);
}
}
}
double sum_weights =
std::accumulate(blocks_weights_.begin(), blocks_weights_.end(), 0.0);
for (const auto& iter : weight_per_surface) {
double surface_probability = iter.second / sum_weights;
if (surface_probability > features::kMaxProbabilityPerSurface)
return false;
}
return true;
}
bool IdentifiabilityStudyGroupSettings::ValidateReidBlockEstimator() {
if (reid_blocks_salts_ranges_.size() != reid_blocks_.size() ||
reid_blocks_bits_.size() != reid_blocks_.size() ||
reid_blocks_noise_probabilities_.size() != reid_blocks_.size())
return false;
bool valid_params =
base::ranges::all_of(reid_blocks_salts_ranges_,
[](uint64_t salt_range) { return salt_range > 0; });
valid_params = valid_params &&
base::ranges::all_of(reid_blocks_bits_, [](int reid_bits) {
return reid_bits > 0 && reid_bits <= 32;
});
return valid_params && base::ranges::all_of(reid_blocks_noise_probabilities_,
[](double reid_noise) {
return reid_noise >= 0 &&
reid_noise <= 1;
});
}
const IdentifiableSurfaceBlocks& IdentifiabilityStudyGroupSettings::blocks()
const {
return blocks_;
}
const std::vector<double>& IdentifiabilityStudyGroupSettings::blocks_weights()
const {
return blocks_weights_;
}
const std::vector<uint64_t>&
IdentifiabilityStudyGroupSettings::reid_blocks_salts_ranges() const {
return reid_blocks_salts_ranges_;
}
const std::vector<int>& IdentifiabilityStudyGroupSettings::reid_blocks_bits()
const {
return reid_blocks_bits_;
}
const std::vector<double>&
IdentifiabilityStudyGroupSettings::reid_blocks_noise_probabilities() const {
return reid_blocks_noise_probabilities_;
}
const IdentifiableSurfaceBlocks&
IdentifiabilityStudyGroupSettings::reid_blocks() const {
return reid_blocks_;
}
const std::vector<blink::IdentifiableSurface::Type>&
IdentifiabilityStudyGroupSettings::allowed_random_types() const {
return allowed_random_types_;
}
bool IdentifiabilityStudyGroupSettings::IsUsingAssignedBlockSampling() const {
return !blocks().empty();
}
bool IdentifiabilityStudyGroupSettings::IsUsingRandomSampling() const {
return expected_surface_count() > 0;
}
bool IdentifiabilityStudyGroupSettings::IsUsingReidScoreEstimator() const {
return !reid_blocks().empty();
}
bool IdentifiabilityStudyGroupSettings::IsUsingSamplingOfSurfaces() const {
// Random and assigned block sampling are mutually exclusive.
DCHECK(!IsUsingRandomSampling() || !IsUsingAssignedBlockSampling());
return IsUsingRandomSampling() || IsUsingAssignedBlockSampling();
}