| // 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/metrics/structured/key_data_provider_ash.h" |
| |
| #include "chrome/browser/profiles/profile_manager.h" |
| #include "components/metrics/structured/key_data_provider_file.h" |
| #include "components/metrics/structured/structured_metrics_validator.h" |
| |
| namespace metrics::structured { |
| namespace { |
| |
| // Default delay period for the PersistentProto. This is the delay before a file |
| // write is triggered after a change has been made. |
| constexpr base::TimeDelta kSaveDelay = base::Milliseconds(1000); |
| |
| // The path used to store per-profile keys. Relative to the user's |
| // cryptohome. This file is created by chromium. |
| constexpr char kProfileKeyPath[] = "structured_metrics/keys"; |
| |
| // The path used to store per-device keys. This file is created by tmpfiles.d |
| // on start and has its permissions and ownership set such that it is writable |
| // by chronos. |
| constexpr char kDeviceKeyPath[] = "/var/lib/metrics/structured/chromium/keys"; |
| |
| } // namespace |
| |
| KeyDataProviderAsh::KeyDataProviderAsh() |
| : KeyDataProviderAsh(base::FilePath(kDeviceKeyPath), kSaveDelay) {} |
| |
| KeyDataProviderAsh::KeyDataProviderAsh(const base::FilePath& device_key_path, |
| base::TimeDelta write_delay) |
| : device_key_path_(device_key_path), write_delay_(write_delay) { |
| device_key_ = |
| std::make_unique<KeyDataProviderFile>(device_key_path_, write_delay_); |
| device_key_->AddObserver(this); |
| } |
| |
| KeyDataProviderAsh::~KeyDataProviderAsh() { |
| device_key_->RemoveObserver(this); |
| if (profile_key_) { |
| profile_key_->RemoveObserver(this); |
| } |
| } |
| |
| bool KeyDataProviderAsh::IsReady() { |
| DCHECK(device_key_); |
| return device_key_->IsReady(); |
| } |
| |
| std::optional<uint64_t> KeyDataProviderAsh::GetId( |
| const std::string& project_name) { |
| KeyDataProvider* key_data_provider = GetKeyDataProvider(project_name); |
| if (!key_data_provider) { |
| return std::nullopt; |
| } |
| return key_data_provider->GetId(project_name); |
| } |
| |
| std::optional<uint64_t> KeyDataProviderAsh::GetSecondaryId( |
| const std::string& project_name) { |
| const auto* project_validator = |
| validator::Validators::Get()->GetProjectValidator(project_name); |
| if (!project_validator) { |
| return std::nullopt; |
| } |
| |
| // If |project_name| is not of type sequence, return std::nullopt as it |
| // should not have a corresponding secondary ID. |
| if (project_validator->event_type() != StructuredEventProto::SEQUENCE) { |
| return std::nullopt; |
| } |
| |
| DCHECK(device_key_); |
| if (device_key_->IsReady()) { |
| return device_key_->GetId(project_name); |
| } |
| |
| return std::nullopt; |
| } |
| |
| KeyData* KeyDataProviderAsh::GetKeyData(const std::string& project_name) { |
| auto* key_data_provider = GetKeyDataProvider(project_name); |
| if (!key_data_provider || !key_data_provider->IsReady()) { |
| return nullptr; |
| } |
| |
| return key_data_provider->GetKeyData(project_name); |
| } |
| |
| void KeyDataProviderAsh::Purge() { |
| if (device_key_) { |
| device_key_->Purge(); |
| } |
| |
| if (profile_key_) { |
| profile_key_->Purge(); |
| } |
| } |
| |
| void KeyDataProviderAsh::OnKeyReady() { |
| NotifyKeyReady(); |
| } |
| |
| void KeyDataProviderAsh::ProfileAdded(const Profile& profile) { |
| // Only the primary user's keys should be loaded. If there is already is a |
| // profile key, no-op. |
| if (profile_key_) { |
| return; |
| } |
| |
| const base::FilePath& profile_path = profile.GetPath(); |
| |
| profile_key_ = std::make_unique<KeyDataProviderFile>( |
| profile_path.Append(kProfileKeyPath), write_delay_); |
| profile_key_->AddObserver(this); |
| } |
| |
| KeyDataProvider* KeyDataProviderAsh::GetKeyDataProvider( |
| const std::string& project_name) { |
| const auto* project_validator = |
| validator::Validators::Get()->GetProjectValidator(project_name); |
| if (!project_validator) { |
| return nullptr; |
| } |
| |
| switch (project_validator->id_scope()) { |
| case IdScope::kPerProfile: { |
| if (profile_key_) { |
| return profile_key_.get(); |
| } |
| break; |
| } |
| case IdScope::kPerDevice: { |
| // Retrieve the profile key if the type is a sequence. |
| if (project_validator->event_type() == StructuredEventProto::SEQUENCE) { |
| return profile_key_ ? profile_key_.get() : nullptr; |
| } |
| if (device_key_) { |
| return device_key_.get(); |
| } |
| break; |
| } |
| default: |
| NOTREACHED(); |
| } |
| |
| return nullptr; |
| } |
| |
| } // namespace metrics::structured |