| // 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 "third_party/blink/public/common/privacy_budget/identifiability_study_settings.h" |
| |
| #include <random> |
| |
| #include "base/check.h" |
| #include "base/compiler_specific.h" |
| #include "base/no_destructor.h" |
| #include "base/optional.h" |
| #include "base/rand_util.h" |
| #include "base/synchronization/atomic_flag.h" |
| #include "base/threading/sequence_local_storage_slot.h" |
| #include "third_party/blink/public/common/privacy_budget/identifiability_study_settings_provider.h" |
| |
| namespace blink { |
| |
| namespace { |
| |
| // IdentifiabilityStudySettings is meant to be used as a global singleton. Its |
| // use is subject to the following constraints. |
| // |
| // 1. The embedder should be able to set the |
| // IdentifiabilityStudySettingsProvider at any point during execution. This |
| // relaxation allows the embedder to perform any required initialization |
| // without blocking process startup. |
| // |
| // 2. Get() and the returned IdentifiabilityStudySettings instance should be |
| // usable from any thread. The returned object must always be well |
| // formed with an infinite lifetime. |
| // |
| // 3. Calling Get() "prior" to the embedder calling SetProvider() should be |
| // harmless and non-blocking. |
| // |
| // 4. Be fast. |
| class ThreadsafeSettingsWrapper { |
| public: |
| ThreadsafeSettingsWrapper() = default; |
| |
| const IdentifiabilityStudySettings* GetSettings() { |
| // Access to initialized_settings_ is behind a memory barrier used for |
| // accessing the atomic flag |initialized_|. The state of |
| // |initialized_settings_| is consistent due to the acquire-release |
| // semantics enforced by |AtomicFlag|. I.e. writes prior to |
| // AtomicFlag::Set() is visible after a AtomicFlag::IsSet() which returns |
| // true. |
| // |
| // If the flag is not set, then |default_settings_| can be used instead. |
| // |
| // In either case, the returned pointer... |
| // 1. ... Points to a well formed IdentifiabilityStudySettings object. |
| // 2. ... Is valid for the remainder of the process lifetime. |
| // 3. ... Is safe to use from any thread. |
| if (!initialized_.IsSet()) |
| return &default_settings_; |
| return &initialized_settings_.value(); |
| } |
| |
| // Same restrictions as IdentifiabilityStudySettings::SetGlobalProvider(). |
| void SetProvider( |
| std::unique_ptr<IdentifiabilityStudySettingsProvider> provider) { |
| DCHECK(!initialized_.IsSet()); |
| initialized_settings_.emplace(std::move(provider)); |
| initialized_.Set(); |
| } |
| |
| void ResetStateForTesting() { |
| initialized_settings_.reset(); |
| initialized_.UnsafeResetForTesting(); |
| } |
| |
| // Function local static initializer is initialized in a threadsafe manner. |
| // This object itself is cheap to construct. |
| static ThreadsafeSettingsWrapper* GetWrapper() { |
| static base::NoDestructor<ThreadsafeSettingsWrapper> wrapper; |
| return wrapper.get(); |
| } |
| |
| private: |
| base::Optional<IdentifiabilityStudySettings> initialized_settings_; |
| const IdentifiabilityStudySettings default_settings_; |
| base::AtomicFlag initialized_; |
| }; |
| |
| // RandGenerator de-biasing implementation stolen from //base/rand_util.h, and |
| // adapted to work with passed-in RNGs. |
| template <typename G> |
| uint64_t RandGenerator(uint64_t range, G& generator) { |
| DCHECK_GT(range, 0u); |
| // We must discard random results above this number, as they would |
| // make the random generator non-uniform (consider e.g. if |
| // MAX_UINT64 was 7 and |range| was 5, then a result of 1 would be twice |
| // as likely as a result of 3 or 4). |
| uint64_t max_acceptable_value = |
| (std::numeric_limits<uint64_t>::max() / range) * range - 1; |
| |
| uint64_t value; |
| do { |
| value = generator(); |
| } while (value > max_acceptable_value); |
| |
| return value % range; |
| } |
| |
| bool DecideSample(int sample_rate) { |
| static base::NoDestructor<base::SequenceLocalStorageSlot<std::mt19937_64>> |
| prng; |
| |
| if (sample_rate == 0) |
| return false; |
| |
| if (sample_rate == 1) |
| return true; |
| |
| if (!prng->GetValuePointer()) |
| prng->emplace(base::RandUint64()); |
| |
| return RandGenerator(sample_rate, **prng) == 0; |
| } |
| |
| } // namespace |
| |
| IdentifiabilityStudySettingsProvider::~IdentifiabilityStudySettingsProvider() = |
| default; |
| |
| IdentifiabilityStudySettings::IdentifiabilityStudySettings() = default; |
| |
| IdentifiabilityStudySettings::IdentifiabilityStudySettings( |
| std::unique_ptr<IdentifiabilityStudySettingsProvider> provider) |
| : provider_(std::move(provider)), |
| is_enabled_(provider_->IsActive()), |
| is_any_surface_or_type_blocked_(provider_->IsAnyTypeOrSurfaceBlocked()) {} |
| |
| IdentifiabilityStudySettings::~IdentifiabilityStudySettings() = default; |
| |
| // static |
| const IdentifiabilityStudySettings* IdentifiabilityStudySettings::Get() { |
| return ThreadsafeSettingsWrapper::GetWrapper()->GetSettings(); |
| } |
| |
| // static |
| void IdentifiabilityStudySettings::SetGlobalProvider( |
| std::unique_ptr<IdentifiabilityStudySettingsProvider> provider) { |
| ThreadsafeSettingsWrapper::GetWrapper()->SetProvider(std::move(provider)); |
| } |
| |
| void IdentifiabilityStudySettings::ResetStateForTesting() { |
| ThreadsafeSettingsWrapper::GetWrapper()->ResetStateForTesting(); |
| } |
| |
| bool IdentifiabilityStudySettings::IsActive() const { |
| return is_enabled_; |
| } |
| |
| bool IdentifiabilityStudySettings::IsSurfaceAllowed( |
| IdentifiableSurface surface) const { |
| if (LIKELY(!is_enabled_)) |
| return false; |
| |
| if (LIKELY(!is_any_surface_or_type_blocked_)) |
| return true; |
| |
| return provider_->IsSurfaceAllowed(surface); |
| } |
| |
| bool IdentifiabilityStudySettings::IsTypeAllowed( |
| IdentifiableSurface::Type type) const { |
| if (LIKELY(!is_enabled_)) |
| return false; |
| |
| if (LIKELY(!is_any_surface_or_type_blocked_)) |
| return true; |
| |
| return provider_->IsTypeAllowed(type); |
| } |
| |
| bool IdentifiabilityStudySettings::IsWebFeatureAllowed( |
| mojom::WebFeature feature) const { |
| return IsSurfaceAllowed(IdentifiableSurface::FromTypeAndToken( |
| IdentifiableSurface::Type::kWebFeature, feature)); |
| } |
| |
| bool IdentifiabilityStudySettings::ShouldSample( |
| IdentifiableSurface surface) const { |
| if (LIKELY(!is_enabled_)) |
| return false; |
| |
| return DecideSample(provider_->SampleRate(surface)); |
| } |
| |
| bool IdentifiabilityStudySettings::ShouldSample( |
| IdentifiableSurface::Type type) const { |
| if (LIKELY(!is_enabled_)) |
| return false; |
| |
| return DecideSample(provider_->SampleRate(type)); |
| } |
| |
| } // namespace blink |