blob: 2f1dfa9f1681e52f37b964dff0c6b6554186105a [file] [log] [blame]
// Copyright 2024 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_SEED_READER_WRITER_H_
#define COMPONENTS_VARIATIONS_SEED_READER_WRITER_H_
#include <string>
#include <string_view>
#include "base/component_export.h"
#include "base/files/file_path.h"
#include "base/files/important_file_writer.h"
#include "base/functional/callback.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/scoped_refptr.h"
#include "base/sequence_checker.h"
#include "base/task/sequenced_task_runner.h"
#include "base/task/task_traits.h"
#include "base/task/thread_pool.h"
#include "base/version_info/channel.h"
#include "components/variations/metrics.h"
#include "components/variations/proto/stored_seed_info.pb.h"
class PrefService;
namespace variations {
class EntropyProviders;
// Trial and group names for the seed file experiment.
const char kSeedFileTrial[] = "SeedFileTrial";
const char kDefaultGroup[] = "Default";
const char kControlGroup[] = "Control_V7";
const char kSeedFilesGroup[] = "SeedFiles_V7";
// A sentinel value that may be stored as the latest variations seed value in
// to indicate that the latest seed is identical to the safe seed. Used to avoid
// duplicating storage space.
inline constexpr char kIdenticalToSafeSeedSentinel[] = "safe_seed_content";
// Represents a seed and its storage format where clients using
// seed-file-based seeds store compressed data and those using
// local-state-based seeds store compressed, base64 encoded data.
// It also stores other seed-related info.
struct COMPONENT_EXPORT(VARIATIONS) StoredSeed {
enum class StorageFormat { kCompressed, kCompressedAndBase64Encoded };
StoredSeed(StorageFormat storage_format,
std::string_view data,
std::string_view signature,
int milestone,
base::Time seed_date,
base::Time client_fetch_time,
std::string_view session_country_code,
std::string_view permanent_country_code,
std::string_view permanent_country_version);
~StoredSeed();
// The storage format of the seed. Seed-file-based seeds are compressed while
// local-state-based seeds are compressed and base64 encoded.
const StorageFormat storage_format;
// The seed data.
const std::string_view data;
// base64-encoded signature of the seed.
const std::string_view signature;
// The milestone with which the seed was fetched
const int milestone = 0;
// Date used for study date checks. Is a server-provided timestamp.
// On some platforms, on the first run, it's set to a client-provided
// timestamp until the server-provided timestamp is fetched. (See
// ChromeFeatureListCreator::SetupInitialPrefs())
const base::Time seed_date;
// The time at which the seed was fetched. This is always a client-side
// timestamp.
const base::Time client_fetch_time;
// Latest country code fetched from the server. Used for evaluating session
// consistency studies.
const std::string session_country_code;
// Country code used for evaluating permanent consistency studies.
const std::string permanent_country_code;
// Chrome version at the time `permanent_country_code` was updated.
const std::string permanent_country_version;
};
// Groups the data from a seed and other seed-related info that is validated
// and ready to be stored in a seed file or local state. This struct is passed
// by value, so it must be copyable and lightweight.
struct ValidatedSeedInfo {
const std::string_view seed_data;
const std::string_view signature;
const int milestone = 0;
const base::Time seed_date;
const base::Time client_fetch_time;
const std::string_view session_country_code;
const std::string_view permanent_country_code;
const std::string_view permanent_country_version;
};
struct SeedFieldsPrefs {
const char* seed;
const char* signature;
const char* milestone;
const char* seed_date;
const char* client_fetch_time;
const char* session_country_code;
const char* permanent_country_code_version;
};
COMPONENT_EXPORT(VARIATIONS)
extern const SeedFieldsPrefs kRegularSeedFieldsPrefs;
COMPONENT_EXPORT(VARIATIONS) extern const SeedFieldsPrefs kSafeSeedFieldsPrefs;
// Handles reading and writing seeds to disk.
class COMPONENT_EXPORT(VARIATIONS) SeedReaderWriter
: public base::ImportantFileWriter::BackgroundDataSerializer {
public:
// `local_state` provides access to the local state prefs. Must not be null.
// `seed_file_dir` denotes the directory for storing a seed file. Note that
// Android Webview intentionally uses an empty path as it uses only local
// state to store seeds.
// `seed_filename` is the base name of a file in which seed data is stored.
// `fields_prefs` is a variations pref struct (kRegularSeedFieldsPrefs or
// kSafeSeedFieldsPrefs) denoting the prefs for the fields for the type of
// seed being stored.
// `channel` describes the release channel of the browser.
// `entropy_providers` is used to provide entropy when setting up the seed
// file field trial. If null, the client will not participate in the
// experiment.
// `file_task_runner` handles IO-related tasks. Must not be null.
SeedReaderWriter(PrefService* local_state,
const base::FilePath& seed_file_dir,
base::FilePath::StringViewType seed_filename,
const SeedFieldsPrefs& fields_prefs,
version_info::Channel channel,
const EntropyProviders* entropy_providers,
scoped_refptr<base::SequencedTaskRunner> file_task_runner =
base::ThreadPool::CreateSequencedTaskRunner(
{base::MayBlock(), base::TaskPriority::USER_VISIBLE,
base::TaskShutdownBehavior::BLOCK_SHUTDOWN}));
SeedReaderWriter(const SeedReaderWriter&) = delete;
SeedReaderWriter& operator=(const SeedReaderWriter&) = delete;
~SeedReaderWriter() override;
// Schedules a write of the compressed seed data to a seed file for some
// clients (see ShouldUseSeedFile()) and schedules a write of the compressed
// and base64-encoded seed data to local state for all other clients. Also
// stores other seed-related info in local state. `permanent_country_version`
// should be empty for the safe seed.
StoreSeedResult StoreValidatedSeedInfo(ValidatedSeedInfo seed_info);
// Clears seed data and other seed-related info. The following fields are
// cleared: seed data, signature, milestone, seed_date and client_fetch_time.
// To clear the session_country_code, use ClearSessionCountry() instead.
// To clear permanent_country_code and version, use
// ClearPermanentConsistencyCountryAndVersion() instead.
void ClearSeedInfo();
// Clears the session country code.
void ClearSessionCountry();
// Returns stored seed data.
StoredSeed GetSeedData() const;
// Overrides the timer used for scheduling writes with `timer_override`.
void SetTimerForTesting(base::OneShotTimer* timer_override);
// Updates the server-provided seed date that is used for study date checks.
void SetSeedDate(base::Time server_date_fetched);
// Updates the time of the last fetch of the seed.
void SetFetchTime(base::Time client_fetch_time);
// Returns true if a write is scheduled but has not yet completed.
bool HasPendingWrite() const;
// Clears the permanent consistency country and version.
void ClearPermanentConsistencyCountryAndVersion();
// Sets the permanent consistency country and version.
void SetPermanentConsistencyCountryAndVersion(std::string_view country,
std::string_view version);
// Reads seed data and returns the result of the load. If a pointer for the
// signature is provided, the signature will be read and stored into
// |base64_seed_signature|. The value stored into |seed_data| should only be
// used if the result is `LoadSeedResult::kSuccess`.
LoadSeedResult ReadSeedData(std::string* seed_data,
std::string* base64_seed_signature = nullptr);
// Stores the seed without applying any extra processing or validation. This
// is used to store invalid data for testing.
void StoreRawSeedForTesting(std::string seed_data);
// Stores a base64-encoded gzipped seed and base64-encoded signature. This is
// the format used when passing the seed by argument in tests.
void StoreBase64EncodedSeedAndSignatureForTesting(
std::string base64_compressed_data,
std::string base64_signature);
private:
// Returns the serialized data to be written to disk. This is done
// asynchronously during the write process.
base::ImportantFileWriter::BackgroundDataProducerCallback
GetSerializedDataProducerForBackgroundSequence() override;
// Schedules `seed_info` to be written using `seed_writer_`. If a field is
// empty, it will not be updated. If you want to clear the seed file, use
// ScheduleSeedFileClear() instead.
StoreSeedResult ScheduleSeedFileWrite(ValidatedSeedInfo seed_info);
// Schedules `seed_info_` to be cleared using `seed_writer_`. See
// VariationsSeedStore::ClearPrefs() .
void ScheduleSeedFileClear();
// Schedules the deletion of a seed file.
void DeleteSeedFile();
// Reads seed data from a seed file, and if the read is successful,
// populates `seed_info_`. May also schedule a seed file write for some
// clients on the first run and for clients that are in the seed file
// experiment's treatment group for the first time. If `seed_pref_` is present
// in `local state_`, additionally clears it.
void ReadSeedFile();
// Schedules a write of `base64_seed_data` to `local_state_`. Fields with
// zero/empty values will be ignored. If you want to clear the seed file, use
// ScheduleSeedFileClear() instead.
StoreSeedResult ScheduleLocalStateWrite(ValidatedSeedInfo seed_info);
// Returns true if a seed file should be used.
bool ShouldUseSeedFile() const;
// Pref service used to persist seeds and seed-related info.
raw_ptr<PrefService> local_state_;
// Prefs used to store the seed and related info in local state.
// TODO(crbug.com/380465790): Remove once the info is stored in the SeedFile.
const raw_ref<const SeedFieldsPrefs> fields_prefs_;
// Task runner for IO-related operations.
const scoped_refptr<base::SequencedTaskRunner> file_task_runner_;
// Helper for safely writing a seed. Null if a seed file path is not given.
std::unique_ptr<base::ImportantFileWriter> seed_writer_;
// Stored seed info. Used to store a seed applied during field trial
// setup or a seed fetched from a variations server. Also stores other
// seed-related info.
StoredSeedInfo seed_info_;
SEQUENCE_CHECKER(sequence_checker_);
};
} // namespace variations
#endif // COMPONENTS_VARIATIONS_SEED_READER_WRITER_H_