Repository: brave/brave-core
Pull request #28362: Metric configuration via component updater
Author: login: DJAndries
Labels: name: puLL-Merge
Created: 2025-03-28T01:16:02Z | Merged: 2025-05-30T22:12:58Z

<!-- Add brave-browser issue below that this PR will resolve -->
Resolves https://github.com/brave/brave-browser/issues/44984


<!-- CI-related labels that can be applied to this PR:
* CI/run-audit-deps (1) - check for known npm/cargo vulnerabilities (audit_deps)
* CI/run-network-audit (1) - run network-audit
* CI/run-upstream-tests - run Chromium unit and browser tests on Linux and Windows (otherwise only on Linux)
* CI/run-linux-arm64, CI/run-macos-x64, CI/run-windows-arm64, CI/run-windows-x86 - run builds that would otherwise be skipped
* CI/skip - do not run CI builds (except noplatform)
* CI/skip-linux-x64, CI/skip-android, CI/skip-macos-arm64, CI/skip-ios, CI/skip-windows-x64 - skip CI builds for specific platforms
* CI/skip-upstream-tests - do not run Chromium unit, or browser tests (otherwise only on Linux)
* CI/skip-all-linters - do not run presubmit and lint checks
* CI/storybook-url (1) - deploy storybook and provide a unique URL for each build

(1) applied automatically when some files are changed (see: https://github.com/brave/brave-core/blob/master/.github/labeler.yml)
-->

<!--
## Checklist:

- Review design docs
  [Browser design principles](https://chromium.googlesource.com/chromium/src/+/refs/heads/main/docs/chrome_browser_design_principles.md)
  [Style guide](https://chromium.googlesource.com/chromium/src/+/main/styleguide/c++/c++.md)
  [Core principles](https://www.chromium.org/developers/core-principles/)
- Ensure there are (tests)[https://www.chromium.org/developers/testing/]. Unit test as much as possible (including edge cases), but also include browser tests covering high level functionality.
- Ensure that there are comments explaining what classes/methods are/do. The "why" is often more important than the "what" in comments. Also update any relevant docs (moving docs from wiki to brave-core if necessary).
- Request security or other review (third-party libraries, rust code, etc...) if applicable [security/privacy review is needed](https://github.com/brave/brave-browser/wiki/Security-reviews) [other review](https://github.com/brave/reviews/issues/new/choose)
  Also see [adding third-party libraries](https://chromium.googlesource.com/chromium/src/+/refs/heads/main/docs/adding_to_third_party.md) for general guidelines on using third party code
- Maks sure there is a [ticket](https://github.com/brave/brave-browser/issues) for your issue
- Use Github [auto-closing keywords](https://docs.github.com/en/github/managing-your-work-on-github/linking-a-pull-request-to-an-issue) in the PR description above
- Write a good [PR/commit description](https://google.github.io/eng-practices/review/developer/cl-descriptions.html)
- Squash any review feedback or "fixup" commits before merge, so that history is a record of what happened in the repo, not your PR
- Add appropriate labels (`QA/Yes` or `QA/No`; `release-notes/include` or `release-notes/exclude`; `OS/...`) to the associated issue
- Checked the PR locally:
  * `npm run test -- brave_browser_tests`, `npm run test -- brave_unit_tests` [wiki](https://github.com/brave/brave-browser/wiki/Tests)
  * `npm run presubmit` [wiki](https://github.com/brave/brave-browser/wiki/Presubmit-checks), `npm run gn_check`, `npm run tslint`
- Run `git rebase master` (if needed)
-->

## Test plan

0. Start browser with fresh profile and ` --use-dev-goupdater-url` switch.
1. Wait 10 seconds. Restart browser. Access local state page.
2. Ensure `Brave.Core.UsageDaily` appears in `logs_constellation_prep` and/or `logs` and NOT `logs_constellation_prep_express`.
3. Ensure all other metrics are present (compare to a recent previous version). Ensure metrics get marked as sent over time.

For Android/iOS:

0. Start browser with internet access disabled. Enable the ` --use-dev-goupdater-url` switch.
1. Close the browser, enable internet access
2. Start the browser and continue from step 1 in the plan above.


Diff:
diff --git a/chromium_src/chrome/browser/component_updater/DEPS b/chromium_src/chrome/browser/component_updater/DEPS
index 8cce79881d8f..61f5bf41a17b 100644
--- a/chromium_src/chrome/browser/component_updater/DEPS
+++ b/chromium_src/chrome/browser/component_updater/DEPS
@@ -2,5 +2,6 @@ include_rules = [
   "+brave/components/brave_component_updater/browser",
   "+brave/components/ai_chat/core/browser",
   "+brave/components/brave_wallet/browser",
+  "+brave/components/p3a",
   "+brave/components/psst/browser/core",
 ]
diff --git a/chromium_src/chrome/browser/component_updater/registration.cc b/chromium_src/chrome/browser/component_updater/registration.cc
index 9e305d06ff2d..079a8e61289d 100644
--- a/chromium_src/chrome/browser/component_updater/registration.cc
+++ b/chromium_src/chrome/browser/component_updater/registration.cc
@@ -12,8 +12,11 @@
 
 #undef RegisterComponentsForUpdate
 
+#include "brave/browser/brave_browser_process.h"
 #include "brave/components/ai_chat/core/browser/local_models_updater.h"
 #include "brave/components/brave_wallet/browser/wallet_data_files_installer.h"
+#include "brave/components/p3a/component_installer.h"
+#include "brave/components/p3a/p3a_service.h"
 #include "brave/components/psst/browser/core/psst_component_installer.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/component_updater/component_updater_utils.h"
@@ -27,7 +30,11 @@ void RegisterComponentsForUpdate() {
       .MaybeRegisterWalletDataFilesComponent(cus,
                                              g_browser_process->local_state());
   psst::RegisterPsstComponent(cus);
-  ai_chat::MigrateDeprecatedLocalModelsComponent(cus);
+  auto* p3a_service = g_brave_browser_process->p3a_service();
+  if (p3a_service) {
+    p3a::RegisterP3AComponent(
+        cus, p3a_service->remote_config_manager()->GetWeakPtr());
+  }
 }
 
 }  // namespace component_updater
diff --git a/components/p3a/BUILD.gn b/components/p3a/BUILD.gn
index fd15cce7ef61..ffd9cbd712c0 100644
--- a/components/p3a/BUILD.gn
+++ b/components/p3a/BUILD.gn
@@ -14,6 +14,8 @@ buildflag_header("buildflags") {
 
 static_library("p3a") {
   sources = [
+    "component_installer.cc",
+    "component_installer.h",
     "constellation_helper.cc",
     "constellation_helper.h",
     "constellation_log_store.cc",
@@ -24,7 +26,10 @@ static_library("p3a") {
     "histograms_braveizer.h",
     "message_manager.cc",
     "message_manager.h",
+    "metric_config.cc",
     "metric_config.h",
+    "metric_config_utils.cc",
+    "metric_config_utils.h",
     "metric_log_store.cc",
     "metric_log_store.h",
     "metric_log_type.cc",
@@ -47,6 +52,8 @@ static_library("p3a") {
     "pref_names.h",
     "region.cc",
     "region.h",
+    "remote_config_manager.cc",
+    "remote_config_manager.h",
     "rotation_scheduler.cc",
     "rotation_scheduler.h",
     "scheduler.cc",
@@ -65,6 +72,7 @@ static_library("p3a") {
     "constellation/rs/cxx:rust_lib",
     "//base",
     "//brave/brave_domains",
+    "//brave/components/brave_component_updater/browser",
     "//brave/components/brave_stats/browser",
     "//brave/components/l10n/common",
     "//brave/components/p3a:buildflags",
@@ -72,6 +80,7 @@ static_library("p3a") {
     "//brave/components/version_info",
     "//brave/vendor/brave_base",
     "//components/cbor",
+    "//components/component_updater",
     "//components/metrics",
     "//components/prefs",
     "//net",
@@ -104,6 +113,7 @@ source_set("unit_tests") {
     "p2a_protocols_unittest.cc",
     "p3a_service_unittest.cc",
     "region_unittest.cc",
+    "remote_config_manager_unittest.cc",
     "rotation_scheduler_unittest.cc",
     "scheduler_unittest.cc",
     "star_randomness_test_util.cc",
@@ -113,6 +123,7 @@ source_set("unit_tests") {
     "constellation/rs/cxx:rust_lib",
     "//base/test:test_support",
     "//brave/components/brave_stats/browser",
+    "//brave/components/constants",
     "//brave/components/p3a",
     "//brave/components/p3a:buildflags",
     "//components/cbor",
diff --git a/components/p3a/component_installer.cc b/components/p3a/component_installer.cc
new file mode 100644
index 000000000000..3bd415efe343
--- /dev/null
+++ b/components/p3a/component_installer.cc
@@ -0,0 +1,114 @@
+/* Copyright (c) 2025 The Brave Authors. All rights reserved.
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at https://mozilla.org/MPL/2.0/. */
+
+#include "brave/components/p3a/component_installer.h"
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "brave/components/brave_component_updater/browser/brave_on_demand_updater.h"
+#include "brave/components/p3a/remote_config_manager.h"
+#include "components/component_updater/component_installer.h"
+#include "components/component_updater/component_updater_service.h"
+
+using component_updater::ComponentUpdateService;
+
+namespace p3a {
+
+namespace {
+
+constexpr char kP3AComponentName[] = "P3A Configuration";
+constexpr char kP3AComponentId[] = "memmkfnkoigleebghlpgeiecaddoblcl";
+constexpr base::FilePath::CharType kComponentInstallDir[] =
+    FILE_PATH_LITERAL("P3AConfig");
+// public key hash:
+// c4cca5dae86b44167bf64842033e1b2b5c908bd6abc30e32d08b12bf50b2cc03
+const uint8_t kP3AComponentHash[32] = {
+    0xc4, 0xcc, 0xa5, 0xda, 0xe8, 0x6b, 0x44, 0x16, 0x7b, 0xf6, 0x48,
+    0x42, 0x03, 0x3e, 0x1b, 0x2b, 0x5c, 0x90, 0x8b, 0xd6, 0xab, 0xc3,
+    0x0e, 0x32, 0xd0, 0x8b, 0x12, 0xbf, 0x50, 0xb2, 0xcc, 0x03};
+
+class P3AComponentInstallerPolicy
+    : public component_updater::ComponentInstallerPolicy {
+ public:
+  explicit P3AComponentInstallerPolicy(
+      base::WeakPtr<RemoteConfigManager> remote_config_manager)
+      : remote_config_manager_(remote_config_manager) {}
+  ~P3AComponentInstallerPolicy() override = default;
+
+  P3AComponentInstallerPolicy(const P3AComponentInstallerPolicy&) = delete;
+  P3AComponentInstallerPolicy& operator=(const P3AComponentInstallerPolicy&) =
+      delete;
+
+ private:
+  // component_updater::ComponentInstallerPolicy:
+  bool SupportsGroupPolicyEnabledComponentUpdates() const override {
+    return false;
+  }
+
+  bool RequiresNetworkEncryption() const override { return false; }
+
+  update_client::CrxInstaller::Result OnCustomInstall(
+      const base::Value::Dict& manifest,
+      const base::FilePath& install_dir) override {
+    return update_client::CrxInstaller::Result(0);
+  }
+
+  void OnCustomUninstall() override {}
+
+  bool VerifyInstallation(const base::Value::Dict& manifest,
+                          const base::FilePath& install_dir) const override {
+    return base::PathExists(install_dir.Append(kP3AManifestFileName));
+  }
+
+  void ComponentReady(const base::Version& version,
+                      const base::FilePath& install_dir,
+                      base::Value::Dict manifest) override {
+    if (remote_config_manager_) {
+      remote_config_manager_->LoadRemoteConfig(install_dir);
+    }
+  }
+
+  base::FilePath GetRelativeInstallDir() const override {
+    return base::FilePath(kComponentInstallDir);
+  }
+
+  void GetHash(std::vector<uint8_t>* hash) const override {
+    hash->assign(std::begin(kP3AComponentHash), std::end(kP3AComponentHash));
+  }
+
+  std::string GetName() const override { return kP3AComponentName; }
+
+  update_client::InstallerAttributes GetInstallerAttributes() const override {
+    return update_client::InstallerAttributes();
+  }
+
+  bool IsBraveComponent() const override { return true; }
+
+  base::WeakPtr<RemoteConfigManager> remote_config_manager_;
+};
+
+}  // namespace
+
+void RegisterP3AComponent(
+    ComponentUpdateService* cus,
+    base::WeakPtr<RemoteConfigManager> remote_config_manager) {
+  if (!cus || !remote_config_manager) {
+    return;
+  }
+
+  auto installer = base::MakeRefCounted<component_updater::ComponentInstaller>(
+      std::make_unique<P3AComponentInstallerPolicy>(remote_config_manager));
+  installer->Register(
+      cus, base::BindOnce([]() {
+        brave_component_updater::BraveOnDemandUpdater::GetInstance()
+            ->EnsureInstalled(kP3AComponentId);
+      }));
+}
+
+}  // namespace p3a
diff --git a/components/p3a/component_installer.h b/components/p3a/component_installer.h
new file mode 100644
index 000000000000..12903dbbf839
--- /dev/null
+++ b/components/p3a/component_installer.h
@@ -0,0 +1,25 @@
+/* Copyright (c) 2025 The Brave Authors. All rights reserved.
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at https://mozilla.org/MPL/2.0/. */
+
+#ifndef BRAVE_COMPONENTS_P3A_COMPONENT_INSTALLER_H_
+#define BRAVE_COMPONENTS_P3A_COMPONENT_INSTALLER_H_
+
+#include "base/memory/weak_ptr.h"
+
+namespace component_updater {
+class ComponentUpdateService;
+}  // namespace component_updater
+
+namespace p3a {
+
+class RemoteConfigManager;
+
+void RegisterP3AComponent(
+    component_updater::ComponentUpdateService* cus,
+    base::WeakPtr<RemoteConfigManager> remote_config_manager);
+
+}  // namespace p3a
+
+#endif  // BRAVE_COMPONENTS_P3A_COMPONENT_INSTALLER_H_
diff --git a/components/p3a/constellation_helper_unittest.cc b/components/p3a/constellation_helper_unittest.cc
index 318cfc51012a..ee4eddd5cf4b 100644
--- a/components/p3a/constellation_helper_unittest.cc
+++ b/components/p3a/constellation_helper_unittest.cc
@@ -279,8 +279,7 @@ TEST_F(P3AConstellationHelperTest, GenerateBasicMessage) {
     helper_->StartMessagePreparation(
         kTestHistogramName, log_type,
         GenerateP3AConstellationMessage(kTestHistogramName, test_epoch,
-                                        meta_info, kP3AUploadType,
-                                        std::nullopt),
+                                        meta_info, kP3AUploadType, nullptr),
         false);
     task_environment_.RunUntilIdle();
 
@@ -300,16 +299,16 @@ TEST_F(P3AConstellationHelperTest, IncludeRefcode) {
   meta_info.Init(&local_state_, "release", install_time_);
 
   std::string message_with_no_refcode = GenerateP3AConstellationMessage(
-      kTestHistogramName, 0, meta_info, kP3AUploadType, std::nullopt);
+      kTestHistogramName, 0, meta_info, kP3AUploadType, nullptr);
   std::vector<std::string> no_refcode_layers =
       SplitMessageLayers(message_with_no_refcode);
 
   EXPECT_EQ(no_refcode_layers.size(), 8U);
   EXPECT_FALSE(no_refcode_layers.at(7).starts_with("ref"));
 
+  MetricConfig refcode_config{.append_attributes = {MetricAttribute::kRef}};
   std::string message_with_refcode = GenerateP3AConstellationMessage(
-      kTestHistogramName, 0, meta_info, kP3AUploadType,
-      MetricConfig{.append_attributes = {MetricAttribute::kRef}});
+      kTestHistogramName, 0, meta_info, kP3AUploadType, &refcode_config);
   std::vector<std::string> refcode_layers =
       SplitMessageLayers(message_with_refcode);
 
@@ -321,8 +320,7 @@ TEST_F(P3AConstellationHelperTest, IncludeRefcode) {
   meta_info.Init(&local_state_, "release", install_time_);
 
   message_with_refcode = GenerateP3AConstellationMessage(
-      kTestHistogramName, 0, meta_info, kP3AUploadType,
-      MetricConfig{.append_attributes = {MetricAttribute::kRef}});
+      kTestHistogramName, 0, meta_info, kP3AUploadType, &refcode_config);
   refcode_layers = SplitMessageLayers(message_with_refcode);
 
   EXPECT_EQ(refcode_layers.size(), 9U);
@@ -332,8 +330,7 @@ TEST_F(P3AConstellationHelperTest, IncludeRefcode) {
   meta_info.Init(&local_state_, "release", install_time_);
 
   message_with_refcode = GenerateP3AConstellationMessage(
-      kTestHistogramName, 0, meta_info, kP3AUploadType,
-      MetricConfig{.append_attributes = {MetricAttribute::kRef}});
+      kTestHistogramName, 0, meta_info, kP3AUploadType, &refcode_config);
   refcode_layers = SplitMessageLayers(message_with_refcode);
 
   EXPECT_EQ(refcode_layers.size(), 9U);
@@ -357,7 +354,7 @@ TEST_F(P3AConstellationHelperTest, CustomAttributes) {
                       }};
 
   std::string message = GenerateP3AConstellationMessage(
-      kTestHistogramName, 7, meta_info, kP3AUploadType, config);
+      kTestHistogramName, 7, meta_info, kP3AUploadType, &config);
 
   std::vector<std::string> layers = SplitMessageLayers(message);
 
@@ -382,9 +379,9 @@ TEST_F(P3AConstellationHelperTest, NebulaMessage) {
   MessageMetainfo meta_info;
   meta_info.Init(&local_state_, "release", install_time_);
 
+  MetricConfig nebula_config{.nebula = true};
   std::string message = GenerateP3AConstellationMessage(
-      kTestHistogramName, 3, meta_info, kP3AUploadType,
-      MetricConfig{.nebula = true});
+      kTestHistogramName, 3, meta_info, kP3AUploadType, &nebula_config);
   std::vector<std::string> no_refcode_layers = SplitMessageLayers(message);
 
   EXPECT_EQ(no_refcode_layers.size(), 7U);
@@ -407,7 +404,7 @@ TEST_F(P3AConstellationHelperTest, NebulaSample) {
         kTestNebulaHistogramName, MetricLogType::kTypical,
         GenerateP3AConstellationMessage(kTestNebulaHistogramName,
                                         kTestTypicalEpoch, meta_info,
-                                        kP3AUploadType, std::nullopt),
+                                        kP3AUploadType, nullptr),
         true);
     task_environment_.RunUntilIdle();
 
@@ -440,7 +437,7 @@ TEST_F(P3AConstellationHelperTest, ActivationDateAttributes) {
 
   // Test case 1: No activation date set
   auto message = GenerateP3AConstellationMessage(
-      kTestHistogramName, 3, meta_info, kP3AUploadType, config);
+      kTestHistogramName, 3, meta_info, kP3AUploadType, &config);
 
   auto layers = SplitMessageLayers(message);
 
@@ -459,7 +456,7 @@ TEST_F(P3AConstellationHelperTest, ActivationDateAttributes) {
   }
 
   message = GenerateP3AConstellationMessage(kTestHistogramName, 3, meta_info,
-                                            kP3AUploadType, config);
+                                            kP3AUploadType, &config);
 
   layers = SplitMessageLayers(message);
 
@@ -479,7 +476,7 @@ TEST_F(P3AConstellationHelperTest, ActivationDateAttributes) {
   config.activation_metric_name = kTestActivationHistogramName;
 
   message = GenerateP3AConstellationMessage(kTestHistogramName, 3, meta_info,
-                                            kP3AUploadType, config);
+                                            kP3AUploadType, &config);
 
   layers = SplitMessageLayers(message);
 
@@ -496,7 +493,7 @@ TEST_F(P3AConstellationHelperTest, ActivationDateAttributes) {
   }
 
   message = GenerateP3AConstellationMessage(kTestHistogramName, 3, meta_info,
-                                            kP3AUploadType, config);
+                                            kP3AUploadType, &config);
 
   layers = SplitMessageLayers(message);
 
diff --git a/components/p3a/message_manager.cc b/components/p3a/message_manager.cc
index 8008caf77b21..6e10aac33df4 100644
--- a/components/p3a/message_manager.cc
+++ b/components/p3a/message_manager.cc
@@ -44,7 +44,6 @@ MessageManager::MessageManager(PrefService& local_state,
                                base::Time first_run_time)
     : local_state_(local_state), config_(config), delegate_(delegate) {
   message_meta_.Init(&local_state, channel, first_run_time);
-
   CleanupActivationDates();
 
   // Init log stores.
@@ -76,6 +75,7 @@ void MessageManager::RegisterPrefs(PrefRegistrySimple* registry) {
 
 void MessageManager::Start(
     scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory) {
+  RemoveObsoleteLogs();
   // Init other components.
   uploader_ = std::make_unique<Uploader>(
       url_loader_factory,
@@ -134,15 +134,26 @@ void MessageManager::Stop() {
   constellation_upload_schedulers_.clear();
 }
 
+void MessageManager::RemoveObsoleteLogs() {
+  for (MetricLogType log_type : kAllMetricLogTypes) {
+    if (json_log_stores_.contains(log_type)) {
+      json_log_stores_[log_type]->RemoveObsoleteLogs();
+    }
+    if (constellation_prep_log_stores_.contains(log_type)) {
+      constellation_prep_log_stores_[log_type]->RemoveObsoleteLogs();
+    }
+  }
+}
+
 void MessageManager::UpdateMetricValue(
     std::string_view histogram_name,
     size_t bucket,
     std::optional<bool> only_update_for_constellation) {
-  bool update_for_all = !only_update_for_constellation.has_value();
-  auto log_type = GetLogTypeForHistogram(histogram_name);
+  auto log_type = delegate_->GetLogTypeForHistogram(histogram_name);
   if (!log_type) {
     return;
   }
+  bool update_for_all = !only_update_for_constellation.has_value();
   if (features::IsConstellationEnabled() &&
       (update_for_all || *only_update_for_constellation)) {
     constellation_prep_log_stores_[*log_type]->UpdateValue(
@@ -152,9 +163,8 @@ void MessageManager::UpdateMetricValue(
   if ((update_for_all || !*only_update_for_constellation) && json_log_store) {
     json_log_store->UpdateValue(std::string(histogram_name), bucket);
   }
-  const auto* metric_config = GetMetricConfig(histogram_name);
-  if (metric_config && *metric_config &&
-      (*metric_config)->record_activation_date && bucket >= 1) {
+  const auto* metric_config = delegate_->GetMetricConfig(histogram_name);
+  if (metric_config && metric_config->record_activation_date && bucket >= 1) {
     // Record activation date for metric, for retention measurement purposes
     ScopedDictPrefUpdate update(&*local_state_, kActivationDatesDictPref);
     if (!update->contains(histogram_name)) {
@@ -328,9 +338,9 @@ void MessageManager::StartScheduledUpload(bool is_constellation,
           : json_log_stores_[log_type]->staged_log_type();
   bool is_nebula = false;
   if (is_constellation) {
-    const auto* metric_config = GetMetricConfig(
+    const auto* metric_config = delegate_->GetMetricConfig(
         constellation_send_log_stores_[log_type]->staged_log_histogram_name());
-    is_nebula = metric_config && *metric_config && (*metric_config)->nebula;
+    is_nebula = metric_config && metric_config->nebula;
   }
 
   VLOG(2) << logging_prefix << " - Uploading " << log.size() << " bytes";
@@ -370,8 +380,8 @@ void MessageManager::StartScheduledConstellationPrep(MetricLogType log_type) {
              "randomness for histogram: "
           << log_key << " " << log;
 
-  const auto* metric_config = GetMetricConfig(log_key);
-  bool is_nebula = metric_config && *metric_config && (*metric_config)->nebula;
+  const auto* metric_config = delegate_->GetMetricConfig(log_key);
+  bool is_nebula = metric_config && metric_config->nebula;
   if (is_nebula && !features::IsNebulaEnabled()) {
     // Do not report if Nebula feature is not enabled,
     // mark request as successful to avoid transmission.
@@ -389,22 +399,7 @@ void MessageManager::StartScheduledConstellationPrep(MetricLogType log_type) {
 
 std::optional<MetricLogType> MessageManager::GetLogTypeForHistogram(
     std::string_view histogram_name) const {
-  std::string histogram_name_str = std::string(histogram_name);
-  std::optional<MetricLogType> result;
-  if (p3a::kCollectedTypicalHistograms.contains(histogram_name) ||
-      delegate_->GetDynamicMetricLogType(histogram_name_str) ==
-          MetricLogType::kTypical) {
-    result = MetricLogType::kTypical;
-  } else if (p3a::kCollectedExpressHistograms.contains(histogram_name) ||
-             delegate_->GetDynamicMetricLogType(histogram_name_str) ==
-                 MetricLogType::kExpress) {
-    result = MetricLogType::kExpress;
-  } else if (p3a::kCollectedSlowHistograms.contains(histogram_name) ||
-             delegate_->GetDynamicMetricLogType(histogram_name_str) ==
-                 MetricLogType::kSlow) {
-    result = MetricLogType::kSlow;
-  }
-  return result;
+  return delegate_->GetLogTypeForHistogram(histogram_name);
 }
 
 std::string MessageManager::SerializeLog(std::string_view histogram_name,
@@ -415,10 +410,9 @@ std::string MessageManager::SerializeLog(std::string_view histogram_name,
   message_meta_.Update();
 
   if (is_constellation) {
-    const auto* metric_config = GetMetricConfig(histogram_name);
-    return GenerateP3AConstellationMessage(
-        histogram_name, value, message_meta_, upload_type,
-        metric_config ? *metric_config : std::nullopt);
+    const auto* metric_config = delegate_->GetMetricConfig(histogram_name);
+    return GenerateP3AConstellationMessage(histogram_name, value, message_meta_,
+                                           upload_type, metric_config);
   } else {
     base::Value::Dict p3a_json_value = GenerateP3AMessageDict(
         histogram_name, value, log_type, message_meta_, upload_type);
@@ -430,28 +424,11 @@ std::string MessageManager::SerializeLog(std::string_view histogram_name,
   }
 }
 
-const std::optional<MetricConfig>* MessageManager::GetMetricConfig(
-    std::string_view histogram_name) const {
-  const std::optional<MetricConfig>* metric_config = nullptr;
-
-  auto it = p3a::kCollectedTypicalHistograms.find(histogram_name);
-  if (it != p3a::kCollectedTypicalHistograms.end()) {
-    metric_config = &it->second;
-  } else if (it = p3a::kCollectedSlowHistograms.find(histogram_name);
-             it != p3a::kCollectedSlowHistograms.end()) {
-    metric_config = &it->second;
-  } else if (it = p3a::kCollectedExpressHistograms.find(histogram_name);
-             it != p3a::kCollectedExpressHistograms.end()) {
-    metric_config = &it->second;
-  }
-  return metric_config;
-}
-
 void MessageManager::CleanupActivationDates() {
   ScopedDictPrefUpdate update(&*local_state_, kActivationDatesDictPref);
 
   for (auto it = update->begin(); it != update->end();) {
-    if (!GetMetricConfig(it->first)) {
+    if (!delegate_->GetLogTypeForHistogram(it->first)) {
       it = update->erase(it);
     } else {
       it++;
@@ -461,9 +438,9 @@ void MessageManager::CleanupActivationDates() {
 
 bool MessageManager::IsEphemeralMetric(
     const std::string& histogram_name) const {
-  const auto* metric_config = GetMetricConfig(histogram_name);
+  const auto* metric_config = delegate_->GetMetricConfig(histogram_name);
 
-  return (metric_config && *metric_config && (*metric_config)->ephemeral) ||
+  return (metric_config && metric_config->ephemeral) ||
          delegate_->GetDynamicMetricLogType(histogram_name).has_value();
 }
 
diff --git a/components/p3a/message_manager.h b/components/p3a/message_manager.h
index 80c9cc8ea60e..c432ec92ad54 100644
--- a/components/p3a/message_manager.h
+++ b/components/p3a/message_manager.h
@@ -60,6 +60,10 @@ class MessageManager : public MetricLogStore::Delegate {
     // or a Constellation preparation for the current epoch.
     virtual void OnMetricCycled(const std::string& histogram_name,
                                 bool is_constellation) = 0;
+    virtual const MetricConfig* GetMetricConfig(
+        std::string_view histogram_name) const = 0;
+    virtual std::optional<MetricLogType> GetLogTypeForHistogram(
+        std::string_view histogram_name) const = 0;
     virtual ~Delegate() {}
   };
   MessageManager(PrefService& local_state,
@@ -77,6 +81,8 @@ class MessageManager : public MetricLogStore::Delegate {
   void Start(scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory);
   void Stop();
 
+  void RemoveObsoleteLogs();
+
   // If only_update_for_constellation is null, the value will be updated for
   // both STAR and Constellation. If true, only the Constellation log store will
   // be updated. If false, only the JSON log store will be updated.
@@ -88,9 +94,6 @@ class MessageManager : public MetricLogStore::Delegate {
       std::string_view histogram_name,
       std::optional<bool> only_update_for_constellation = std::nullopt);
 
-  const std::optional<MetricConfig>* GetMetricConfig(
-      std::string_view histogram_name) const;
-
  private:
   void StartScheduledUpload(bool is_constellation, MetricLogType log_type);
   void StartScheduledConstellationPrep(MetricLogType log_type);
diff --git a/components/p3a/message_manager_unittest.cc b/components/p3a/message_manager_unittest.cc
index 42f285273edf..25ea3e9ecf62 100644
--- a/components/p3a/message_manager_unittest.cc
+++ b/components/p3a/message_manager_unittest.cc
@@ -19,6 +19,7 @@
 #include "base/test/values_test_util.h"
 #include "base/time/time.h"
 #include "brave/components/p3a/features.h"
+#include "brave/components/p3a/metric_config_utils.h"
 #include "brave/components/p3a/metric_log_type.h"
 #include "brave/components/p3a/metric_names.h"
 #include "brave/components/p3a/p3a_config.h"
@@ -59,7 +60,17 @@ class P3AMessageManagerTest : public testing::Test,
 
   std::optional<MetricLogType> GetDynamicMetricLogType(
       std::string_view histogram_name) const override {
-    return std::optional<MetricLogType>();
+    return std::nullopt;
+  }
+
+  const MetricConfig* GetMetricConfig(
+      std::string_view histogram_name) const override {
+    return GetBaseMetricConfig(histogram_name);
+  }
+
+  std::optional<MetricLogType> GetLogTypeForHistogram(
+      std::string_view histogram_name) const override {
+    return GetBaseLogTypeForHistogram(histogram_name);
   }
 
   void OnRotation(MetricLogType log_type, bool is_constellation) override {}
diff --git a/components/p3a/metric_config.cc b/components/p3a/metric_config.cc
new file mode 100644
index 000000000000..6ade9f8a1108
--- /dev/null
+++ b/components/p3a/metric_config.cc
@@ -0,0 +1,156 @@
+/* Copyright (c) 2025 The Brave Authors. All rights reserved.
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at https://mozilla.org/MPL/2.0/. */
+
+#include "brave/components/p3a/metric_config.h"
+
+#include "base/containers/fixed_flat_map.h"
+
+namespace p3a {
+
+namespace {
+
+constexpr auto kMetricAttributeMap =
+    base::MakeFixedFlatMap<std::string_view, MetricAttribute>({
+        {"answer_index", MetricAttribute::kAnswerIndex},
+        {"version", MetricAttribute::kVersion},
+        {"yoi", MetricAttribute::kYoi},
+        {"channel", MetricAttribute::kChannel},
+        {"platform", MetricAttribute::kPlatform},
+        {"country_code", MetricAttribute::kCountryCode},
+        {"woi", MetricAttribute::kWoi},
+        {"general_platform", MetricAttribute::kGeneralPlatform},
+        {"region", MetricAttribute::kRegion},
+        {"subregion", MetricAttribute::kSubregion},
+        {"ref", MetricAttribute::kRef},
+        {"dtoi", MetricAttribute::kDateOfInstall},
+        {"dtoa", MetricAttribute::kDateOfActivation},
+        {"woa", MetricAttribute::kWeekOfActivation},
+    });
+
+bool GetMetricAttribute(const base::Value* value,
+                        std::optional<MetricAttribute>* field) {
+  if (!value || !value->is_string()) {
+    return false;
+  }
+
+  const auto& str = value->GetString();
+  auto it = kMetricAttributeMap.find(str);
+  if (it == kMetricAttributeMap.end()) {
+    return false;
+  }
+  *field = it->second;
+  return true;
+}
+
+bool GetMetricLogType(const base::Value* value,
+                      std::optional<MetricLogType>* field) {
+  if (!value || !value->is_string()) {
+    return false;
+  }
+
+  auto log_type = StringToMetricLogType(value->GetString());
+  if (!log_type) {
+    return false;
+  }
+  *field = log_type.value();
+  return true;
+}
+
+bool GetMetricAttributes(const base::Value* value,
+                         std::optional<MetricAttributes>* field) {
+  if (!value || !value->is_list()) {
+    return false;
+  }
+
+  const auto& list = value->GetList();
+
+  MetricAttributes attributes;
+
+  for (size_t i = 0; i < list.size() && i < attributes.size(); i++) {
+    if (!GetMetricAttribute(&list[i], &attributes[i])) {
+      return false;
+    }
+  }
+
+  *field = attributes;
+  return true;
+}
+
+bool GetAppendAttributes(const base::Value* value,
+                         std::optional<MetricAttributesToAppend>* field) {
+  if (!value || !value->is_list()) {
+    return false;
+  }
+
+  const auto& list = value->GetList();
+
+  MetricAttributesToAppend attributes;
+
+  for (size_t i = 0; i < list.size() && i < attributes.size(); i++) {
+    if (!GetMetricAttribute(&list[i], &attributes[i])) {
+      return false;
+    }
+  }
+
+  *field = attributes;
+
+  return true;
+}
+
+bool GetOptionalString(const base::Value* value,
+                       std::optional<std::string>* field) {
+  if (!value || !value->is_string()) {
+    return false;
+  }
+
+  *field = value->GetString();
+  return true;
+}
+
+bool GetOptionalBool(const base::Value* value, std::optional<bool>* field) {
+  if (!value || !value->is_bool()) {
+    return false;
+  }
+  *field = value->GetBool();
+  return true;
+}
+
+}  // namespace
+
+RemoteMetricConfig::RemoteMetricConfig() = default;
+RemoteMetricConfig::~RemoteMetricConfig() = default;
+
+RemoteMetricConfig::RemoteMetricConfig(const RemoteMetricConfig&) = default;
+RemoteMetricConfig& RemoteMetricConfig::operator=(const RemoteMetricConfig&) =
+    default;
+
+void RemoteMetricConfig::RegisterJSONConverter(
+    base::JSONValueConverter<RemoteMetricConfig>* converter) {
+  converter->RegisterCustomValueField(
+      "ephemeral", &RemoteMetricConfig::ephemeral, &GetOptionalBool);
+  converter->RegisterCustomValueField("constellation_only",
+                                      &RemoteMetricConfig::constellation_only,
+                                      &GetOptionalBool);
+  converter->RegisterCustomValueField("nebula", &RemoteMetricConfig::nebula,
+                                      &GetOptionalBool);
+  converter->RegisterCustomValueField(
+      "disable_country_strip", &RemoteMetricConfig::disable_country_strip,
+      &GetOptionalBool);
+  converter->RegisterCustomValueField(
+      "attributes", &RemoteMetricConfig::attributes, &GetMetricAttributes);
+  converter->RegisterCustomValueField("append_attributes",
+                                      &RemoteMetricConfig::append_attributes,
+                                      &GetAppendAttributes);
+  converter->RegisterCustomValueField(
+      "record_activation_date", &RemoteMetricConfig::record_activation_date,
+      &GetOptionalBool);
+  converter->RegisterCustomValueField(
+      "activation_metric_name", &RemoteMetricConfig::activation_metric_name,
+      &GetOptionalString);
+  converter->RegisterCustomValueField("cadence", &RemoteMetricConfig::cadence,
+                                      &GetMetricLogType);
+}
+
+}  // namespace p3a
diff --git a/components/p3a/metric_config.h b/components/p3a/metric_config.h
index b824311486d8..aad9102bd9f3 100644
--- a/components/p3a/metric_config.h
+++ b/components/p3a/metric_config.h
@@ -8,8 +8,12 @@
 
 #include <array>
 #include <optional>
+#include <string>
 #include <string_view>
 
+#include "base/json/json_value_converter.h"
+#include "brave/components/p3a/metric_log_type.h"
+
 namespace p3a {
 
 enum class MetricAttribute {
@@ -64,6 +68,33 @@ struct MetricConfig {
   // If provided, the activation date recorded from another metric
   // will be reported.
   std::optional<std::string_view> activation_metric_name;
+
+  // If specified in a remote configuration, the cadence of the metric will be
+  // overridden.
+  std::optional<MetricLogType> cadence;
+};
+
+// This struct is used to store the remote configuration for a metric.
+// The remote configuration is provided by the component updater.
+struct RemoteMetricConfig {
+  RemoteMetricConfig();
+  ~RemoteMetricConfig();
+
+  RemoteMetricConfig(const RemoteMetricConfig&);
+  RemoteMetricConfig& operator=(const RemoteMetricConfig&);
+
+  std::optional<bool> ephemeral;
+  std::optional<bool> constellation_only;
+  std::optional<bool> nebula;
+  std::optional<bool> disable_country_strip;
+  std::optional<MetricAttributes> attributes;
+  std::optional<MetricAttributesToAppend> append_attributes;
+  std::optional<bool> record_activation_date;
+  std::optional<std::string> activation_metric_name;
+  std::optional<MetricLogType> cadence;
+
+  static void RegisterJSONConverter(
+      base::JSONValueConverter<RemoteMetricConfig>* converter);
 };
 
 }  // namespace p3a
diff --git a/components/p3a/metric_config_utils.cc b/components/p3a/metric_config_utils.cc
new file mode 100644
index 000000000000..8a21ae4afcb6
--- /dev/null
+++ b/components/p3a/metric_config_utils.cc
@@ -0,0 +1,43 @@
+// Copyright (c) 2025 The Brave Authors. All rights reserved.
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this file,
+// You can obtain one at https://mozilla.org/MPL/2.0/.
+
+#include "brave/components/p3a/metric_config_utils.h"
+
+#include "brave/components/p3a/metric_names.h"
+
+namespace p3a {
+
+const MetricConfig* GetBaseMetricConfig(std::string_view histogram_name) {
+  const std::optional<MetricConfig>* metric_config = nullptr;
+
+  auto it = p3a::kCollectedTypicalHistograms.find(histogram_name);
+  if (it != p3a::kCollectedTypicalHistograms.end()) {
+    metric_config = &it->second;
+  } else if (it = p3a::kCollectedSlowHistograms.find(histogram_name);
+             it != p3a::kCollectedSlowHistograms.end()) {
+    metric_config = &it->second;
+  } else if (it = p3a::kCollectedExpressHistograms.find(histogram_name);
+             it != p3a::kCollectedExpressHistograms.end()) {
+    metric_config = &it->second;
+  }
+  if (metric_config && metric_config->has_value()) {
+    return &metric_config->value();
+  }
+  return nullptr;
+}
+
+std::optional<MetricLogType> GetBaseLogTypeForHistogram(
+    std::string_view histogram_name) {
+  if (p3a::kCollectedExpressHistograms.contains(histogram_name)) {
+    return MetricLogType::kExpress;
+  } else if (p3a::kCollectedSlowHistograms.contains(histogram_name)) {
+    return MetricLogType::kSlow;
+  } else if (p3a::kCollectedTypicalHistograms.contains(histogram_name)) {
+    return MetricLogType::kTypical;
+  }
+  return std::nullopt;
+}
+
+}  // namespace p3a
diff --git a/components/p3a/metric_config_utils.h b/components/p3a/metric_config_utils.h
new file mode 100644
index 000000000000..18006528bd5e
--- /dev/null
+++ b/components/p3a/metric_config_utils.h
@@ -0,0 +1,29 @@
+// Copyright (c) 2025 The Brave Authors. All rights reserved.
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this file,
+// You can obtain one at https://mozilla.org/MPL/2.0/.
+
+#ifndef BRAVE_COMPONENTS_P3A_METRIC_CONFIG_UTILS_H_
+#define BRAVE_COMPONENTS_P3A_METRIC_CONFIG_UTILS_H_
+
+#include <optional>
+#include <string_view>
+
+#include "brave/components/p3a/metric_log_type.h"
+
+namespace p3a {
+
+struct MetricConfig;
+
+// Returns the base configuration for a metric from the built-in metric arrays
+// (kCollectedExpressHistograms, kCollectedTypicalHistograms,
+// kCollectedSlowHistograms)
+const MetricConfig* GetBaseMetricConfig(std::string_view histogram_name);
+
+// Returns the log type for a histogram from the built-in metric arrays
+std::optional<MetricLogType> GetBaseLogTypeForHistogram(
+    std::string_view histogram_name);
+
+}  // namespace p3a
+
+#endif  // BRAVE_COMPONENTS_P3A_METRIC_CONFIG_UTILS_H_
diff --git a/components/p3a/metric_log_store.cc b/components/p3a/metric_log_store.cc
index 424547b9e5e1..9efe3b2b7e91 100644
--- a/components/p3a/metric_log_store.cc
+++ b/components/p3a/metric_log_store.cc
@@ -156,7 +156,9 @@ void MetricLogStore::ResetUploadStamps() {
       DCHECK(!it->second.sent_timestamp.is_null());
       DCHECK(!unsent_entries_.contains(it->first));
 
-      if (delegate_->IsEphemeralMetric(it->first)) {
+      auto metric_log_type = delegate_->GetLogTypeForHistogram(it->first);
+      if (!metric_log_type || metric_log_type != type_ ||
+          delegate_->IsEphemeralMetric(it->first)) {
         // Ephemeral metrics should only be sent once.
         // Remove value from log store so it doesn't get
         // sent again (unless another histogram value is recorded)
@@ -283,19 +285,9 @@ void MetricLogStore::LoadPersistedUnsentLogs() {
 
   const char* pref_name = GetPrefName();
 
-  std::vector<std::string> metrics_to_remove;
-
   const base::Value::Dict& log_dict = local_state_->GetDict(pref_name);
   for (const auto [name, value] : log_dict) {
     LogEntry entry;
-    // Check if the metric is obsolete.
-    auto metric_log_type = delegate_->GetLogTypeForHistogram(name);
-    if (!metric_log_type || metric_log_type != type_) {
-      // Drop it from the local state.
-      metrics_to_remove.push_back(name);
-      continue;
-    }
-    // Value.
     const base::Value::Dict& dict = value.GetDict();
     if (const std::string* v = dict.FindString(kLogValueKey)) {
       if (!base::StringToUint64(*v, &entry.value)) {
@@ -326,13 +318,19 @@ void MetricLogStore::LoadPersistedUnsentLogs() {
       unsent_entries_.insert(name);
     }
   }
+}
 
-  if (!metrics_to_remove.empty()) {
-    ScopedDictPrefUpdate update(&*local_state_, pref_name);
-    for (const std::string& name : metrics_to_remove) {
-      update->Remove(name);
+void MetricLogStore::RemoveObsoleteLogs() {
+  std::vector<std::string> metrics_to_remove;
+  for (const auto& [name, entry] : log_) {
+    auto metric_log_type = delegate_->GetLogTypeForHistogram(name);
+    if (!metric_log_type || *metric_log_type != type_) {
+      metrics_to_remove.push_back(name);
     }
   }
+  for (const auto& name : metrics_to_remove) {
+    RemoveValueIfExists(name);
+  }
 }
 
 const metrics::LogMetadata MetricLogStore::staged_log_metadata() const {
diff --git a/components/p3a/metric_log_store.h b/components/p3a/metric_log_store.h
index 86890290339e..3ad27dc8bed8 100644
--- a/components/p3a/metric_log_store.h
+++ b/components/p3a/metric_log_store.h
@@ -81,6 +81,7 @@ class MetricLogStore : public metrics::LogStore {
   void TrimAndPersistUnsentLogs(bool overwrite_in_memory_store) override;
   // Returns early if founds malformed persisted values.
   void LoadPersistedUnsentLogs() override;
+  void RemoveObsoleteLogs();
 
  private:
   struct LogEntry {
diff --git a/components/p3a/metric_log_store_unittest.cc b/components/p3a/metric_log_store_unittest.cc
index 29933de3fbec..0735e8e710d9 100644
--- a/components/p3a/metric_log_store_unittest.cc
+++ b/components/p3a/metric_log_store_unittest.cc
@@ -18,6 +18,7 @@
 namespace p3a {
 
 constexpr char kTestExpressMetric[] = "Brave.Test.ExpressMetric";
+constexpr char kTestUnknownMetric[] = "Brave.Test.UnknownMetric";
 
 class P3AMetricLogStoreTest : public testing::Test,
                               public MetricLogStore::Delegate {
@@ -104,6 +105,7 @@ TEST_F(P3AMetricLogStoreTest, GetAllLogsAfterReload) {
 
   SetUpLogStore();
   log_store->LoadPersistedUnsentLogs();
+  log_store->RemoveObsoleteLogs();
 
   ConsumeMessages(15);
 }
@@ -117,13 +119,14 @@ TEST_F(P3AMetricLogStoreTest, GetAllLogsAfterTimeReset) {
 }
 
 TEST_F(P3AMetricLogStoreTest, ShouldNotLoadUnknownMetric) {
-  log_store->UpdateValue("Brave.UnknownMetric", 3);
+  log_store->UpdateValue(kTestUnknownMetric, 3);
   log_store->UpdateValue(kTestExpressMetric, 4);
 
   ASSERT_TRUE(log_store->has_unsent_logs());
 
   SetUpLogStore();
   log_store->LoadPersistedUnsentLogs();
+  log_store->RemoveObsoleteLogs();
 
   ASSERT_FALSE(log_store->has_unsent_logs());
 }
diff --git a/components/p3a/p3a_message.cc b/components/p3a/p3a_message.cc
index 0cb2cd6ee0f4..17a67c0d5e3f 100644
--- a/components/p3a/p3a_message.cc
+++ b/components/p3a/p3a_message.cc
@@ -90,11 +90,10 @@ bool ShouldIncludeDate(const base::Time& time) {
   return (base::Time::Now() - time) < kDateOmissionThreshold;
 }
 
-std::string InferActivationDate(
-    const MessageMetainfo& meta,
-    const std::optional<MetricConfig>& metric_config,
-    std::string_view metric_name,
-    bool truncate_to_week) {
+std::string InferActivationDate(const MessageMetainfo& meta,
+                                const MetricConfig* metric_config,
+                                std::string_view metric_name,
+                                bool truncate_to_week) {
   std::string_view activation_metric_name = metric_name;
   if (metric_config && metric_config->activation_metric_name.has_value()) {
     activation_metric_name = *metric_config->activation_metric_name;
@@ -113,7 +112,7 @@ std::vector<std::array<std::string, 2>> PopulateConstellationAttributes(
     const std::string_view metric_name,
     const uint64_t metric_value,
     const MessageMetainfo& meta,
-    const std::optional<MetricConfig>& metric_config,
+    const MetricConfig* metric_config,
     const std::vector<MetricAttribute>& attributes_to_load,
     bool is_creative) {
   base::Time::Exploded dtoi_exploded;
@@ -290,12 +289,11 @@ base::Value::Dict GenerateP3AMessageDict(std::string_view metric_name,
   return result;
 }
 
-std::string GenerateP3AConstellationMessage(
-    std::string_view metric_name,
-    uint64_t metric_value,
-    const MessageMetainfo& meta,
-    const std::string& upload_type,
-    const std::optional<MetricConfig>& metric_config) {
+std::string GenerateP3AConstellationMessage(std::string_view metric_name,
+                                            uint64_t metric_value,
+                                            const MessageMetainfo& meta,
+                                            const std::string& upload_type,
+                                            const MetricConfig* metric_config) {
   std::vector<MetricAttribute> attributes_to_load;
   if (metric_config && metric_config->attributes) {
     for (const auto& attr : *metric_config->attributes) {
diff --git a/components/p3a/p3a_message.h b/components/p3a/p3a_message.h
index c958cbc70b7c..7cd291276252 100644
--- a/components/p3a/p3a_message.h
+++ b/components/p3a/p3a_message.h
@@ -94,12 +94,11 @@ base::Value::Dict GenerateP3AMessageDict(std::string_view metric_name,
                                          const MessageMetainfo& meta,
                                          const std::string& upload_type);
 
-std::string GenerateP3AConstellationMessage(
-    std::string_view metric_name,
-    uint64_t metric_value,
-    const MessageMetainfo& meta,
-    const std::string& upload_type,
-    const std::optional<MetricConfig>& metric_config);
+std::string GenerateP3AConstellationMessage(std::string_view metric_name,
+                                            uint64_t metric_value,
+                                            const MessageMetainfo& meta,
+                                            const std::string& upload_type,
+                                            const MetricConfig* metric_config);
 
 }  // namespace p3a
 
diff --git a/components/p3a/p3a_service.cc b/components/p3a/p3a_service.cc
index 5cfbaafe35f2..7590ecfc89dd 100644
--- a/components/p3a/p3a_service.cc
+++ b/components/p3a/p3a_service.cc
@@ -22,10 +22,12 @@
 #include "base/trace_event/trace_event.h"
 #include "brave/components/brave_stats/browser/brave_stats_updater_util.h"
 #include "brave/components/p3a/message_manager.h"
+#include "brave/components/p3a/metric_config_utils.h"
 #include "brave/components/p3a/metric_names.h"
 #include "brave/components/p3a/p2a_protocols.h"
 #include "brave/components/p3a/p3a_config.h"
 #include "brave/components/p3a/pref_names.h"
+#include "brave/components/p3a/remote_config_manager.h"
 #include "components/prefs/pref_registry_simple.h"
 #include "components/prefs/pref_service.h"
 #include "components/prefs/scoped_user_pref_update.h"
@@ -47,7 +49,6 @@ namespace {
 // Receiving this value will effectively prevent the metric from transmission
 // to the backend. For now we consider this as a hack for p2a metrics, which
 // should be refactored in better times.
-const int32_t kSuspendedMetricValue = INT_MAX - 1;
 const uint64_t kSuspendedMetricBucket = INT_MAX - 1;
 
 constexpr char kDynamicMetricsDictPref[] = "p3a.dynamic_metrics";
@@ -83,8 +84,11 @@ P3AService::P3AService(PrefService& local_state,
   if (first_run_time.is_null()) {
     first_run_time = base::Time::Now();
   }
+  remote_config_manager_ = std::make_unique<RemoteConfigManager>(this);
+
   message_manager_ = std::make_unique<MessageManager>(
       local_state, &config_, *this, channel, first_run_time);
+
   pref_change_registrar_.Init(&local_state);
   pref_change_registrar_.Add(
       kP3AEnabled, base::BindRepeating(&P3AService::OnP3AEnabledChanged,
@@ -190,21 +194,33 @@ bool P3AService::IsP3AEnabled() const {
 
 void P3AService::Init(
     scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory) {
-  url_loader_factory_ = url_loader_factory;
+  if (url_loader_factory) {
+    url_loader_factory_ = url_loader_factory;
+  }
+
+  if (initialized_ || !url_loader_factory_) {
+    return;
+  }
+
+  // TODO(djandries): remove the buildflag guard once
+  // https://github.com/brave/brave-browser/issues/45042 is resolved
+#if !BUILDFLAG(IS_IOS)
+  if (!remote_config_manager_->is_loaded()) {
+    return;
+  }
+#endif
+
   if (local_state_->GetBoolean(kP3AEnabled)) {
-    message_manager_->Start(url_loader_factory);
+    message_manager_->Start(url_loader_factory_);
   }
 
-  // Init basic prefs.
   initialized_ = true;
 
-  VLOG(2) << "P3AService::Init() Done!";
-
   // Store values that were recorded between calling constructor and |Init()|.
   for (const auto& entry : histogram_values_) {
     HandleHistogramChange(std::string(entry.first), entry.second);
   }
-  histogram_values_ = {};
+  histogram_values_.clear();
 }
 
 void P3AService::OnRotation(MetricLogType log_type, bool is_constellation) {
@@ -224,6 +240,52 @@ std::optional<MetricLogType> P3AService::GetDynamicMetricLogType(
              : std::optional<MetricLogType>();
 }
 
+const MetricConfig* P3AService::GetMetricConfig(
+    std::string_view histogram_name) const {
+  // First check if there's a remote config for this metric
+  if (remote_config_manager_) {
+    const auto* remote_config = remote_config_manager_->GetRemoteMetricConfig(
+        std::string(histogram_name));
+    if (remote_config) {
+      return remote_config;
+    }
+  }
+
+  // Fall back to the base config if no remote config exists
+  return GetBaseMetricConfig(histogram_name);
+}
+
+const MetricConfig* P3AService::GetBaseMetricConfig(
+    std::string_view histogram_name) const {
+  return p3a::GetBaseMetricConfig(histogram_name);
+}
+
+void P3AService::OnRemoteConfigLoaded() {
+  if (!initialized_) {
+    Init(nullptr);
+  } else {
+    message_manager_->RemoveObsoleteLogs();
+  }
+}
+
+std::optional<MetricLogType> P3AService::GetLogTypeForHistogram(
+    std::string_view histogram_name) const {
+  if (remote_config_manager_) {
+    const auto* remote_config =
+        remote_config_manager_->GetRemoteMetricConfig(histogram_name);
+    if (remote_config && remote_config->cadence) {
+      return *remote_config->cadence;
+    }
+  }
+
+  auto dynamic_metric_log_type = GetDynamicMetricLogType(histogram_name);
+  if (dynamic_metric_log_type) {
+    return dynamic_metric_log_type;
+  }
+
+  return GetBaseLogTypeForHistogram(histogram_name);
+}
+
 void P3AService::LoadDynamicMetrics() {
   const auto& dict = local_state_->GetDict(kDynamicMetricsDictPref);
 
@@ -259,9 +321,9 @@ void P3AService::OnHistogramChanged(std::string_view histogram_name,
   // description for details.
   if (IsSuspendedMetric(histogram_name, sample)) {
     GetUIThreadTaskRunner()->PostTask(
-        FROM_HERE, base::BindOnce(&P3AService::OnHistogramChangedOnUI, this,
-                                  histogram_name, kSuspendedMetricValue,
-                                  kSuspendedMetricBucket));
+        FROM_HERE,
+        base::BindOnce(&P3AService::HandleHistogramChange, this, histogram_name,
+                       kSuspendedMetricBucket, std::nullopt));
     return;
   }
 
@@ -289,34 +351,28 @@ void P3AService::OnHistogramChanged(std::string_view histogram_name,
   }
 
   GetUIThreadTaskRunner()->PostTask(
-      FROM_HERE, base::BindOnce(&P3AService::OnHistogramChangedOnUI, this,
-                                histogram_name, sample, bucket));
+      FROM_HERE, base::BindOnce(&P3AService::HandleHistogramChange, this,
+                                histogram_name, bucket, std::nullopt));
 }
 
-void P3AService::OnHistogramChangedOnUI(std::string_view histogram_name,
-                                        base::HistogramBase::Sample32 sample,
-                                        size_t bucket) {
+void P3AService::HandleHistogramChange(
+    std::string_view histogram_name,
+    size_t bucket,
+    std::optional<bool> only_update_for_constellation) {
   VLOG(2) << "P3AService::OnHistogramChanged: histogram_name = "
-          << histogram_name << " Sample = " << sample << " bucket = " << bucket;
+          << histogram_name << " Sample = " << bucket;
   if (!initialized_) {
     // Will handle it later when ready.
     histogram_values_[histogram_name] = bucket;
-  } else {
-    HandleHistogramChange(histogram_name, bucket);
+    return;
   }
-}
-
-void P3AService::HandleHistogramChange(
-    std::string_view histogram_name,
-    size_t bucket,
-    std::optional<bool> only_update_for_constellation) {
   if (IsSuspendedMetric(histogram_name, bucket)) {
     message_manager_->RemoveMetricValue(histogram_name,
                                         only_update_for_constellation);
     return;
   }
-  const auto* metric_config = message_manager_->GetMetricConfig(histogram_name);
-  if (metric_config && *metric_config && (*metric_config)->constellation_only) {
+  const auto* metric_config = GetMetricConfig(histogram_name);
+  if (metric_config && metric_config->constellation_only) {
     only_update_for_constellation = true;
   }
   message_manager_->UpdateMetricValue(histogram_name, bucket,
diff --git a/components/p3a/p3a_service.h b/components/p3a/p3a_service.h
index 2ccf36283da0..5832a13e5a2b 100644
--- a/components/p3a/p3a_service.h
+++ b/components/p3a/p3a_service.h
@@ -21,6 +21,7 @@
 #include "brave/components/p3a/message_manager.h"
 #include "brave/components/p3a/metric_log_type.h"
 #include "brave/components/p3a/p3a_config.h"
+#include "brave/components/p3a/remote_config_manager.h"
 #include "components/prefs/pref_change_registrar.h"
 
 class PrefRegistrySimple;
@@ -41,7 +42,8 @@ struct P3AConfig;
 // P3AMessageManager.
 // TODO(iefremov): It should be possible to get rid of refcounted here.
 class P3AService : public base::RefCountedThreadSafe<P3AService>,
-                   public MessageManager::Delegate {
+                   public MessageManager::Delegate,
+                   public RemoteConfigManager::Delegate {
  public:
   P3AService(PrefService& local_state,
              std::string channel,
@@ -100,12 +102,26 @@ class P3AService : public base::RefCountedThreadSafe<P3AService>,
                           uint64_t name_hash,
                           base::HistogramBase::Sample32 sample);
 
-  // P3AMessageManager::Delegate
+  // Returns the RemoteConfigManager instance owned by this P3AService
+  RemoteConfigManager* remote_config_manager() {
+    return remote_config_manager_.get();
+  }
+
+  // MessageManager::Delegate
   void OnRotation(MetricLogType log_type, bool is_constellation) override;
   void OnMetricCycled(const std::string& histogram_name,
                       bool is_constellation) override;
   std::optional<MetricLogType> GetDynamicMetricLogType(
       std::string_view histogram_name) const override;
+  const MetricConfig* GetMetricConfig(
+      std::string_view histogram_name) const override;
+  std::optional<MetricLogType> GetLogTypeForHistogram(
+      std::string_view histogram_name) const override;
+
+  // RemoteConfigManager::Delegate
+  const MetricConfig* GetBaseMetricConfig(
+      std::string_view histogram_name) const override;
+  void OnRemoteConfigLoaded() override;
 
   void DisableStarAttestationForTesting();
 
@@ -119,10 +135,6 @@ class P3AService : public base::RefCountedThreadSafe<P3AService>,
 
   void OnP3AEnabledChanged();
 
-  void OnHistogramChangedOnUI(std::string_view histogram_name,
-                              base::HistogramBase::Sample32 sample,
-                              size_t bucket);
-
   // Updates or removes a metric from the log.
   void HandleHistogramChange(
       std::string_view histogram_name,
@@ -146,6 +158,7 @@ class P3AService : public base::RefCountedThreadSafe<P3AService>,
       dynamic_metric_sample_callbacks_;
 
   std::unique_ptr<MessageManager> message_manager_;
+  std::unique_ptr<RemoteConfigManager> remote_config_manager_;
 
   // Used to store histogram values that are produced between constructing
   // the service and its initialization.
diff --git a/components/p3a/p3a_service_unittest.cc b/components/p3a/p3a_service_unittest.cc
index 7db95519912d..87f716f389ee 100644
--- a/components/p3a/p3a_service_unittest.cc
+++ b/components/p3a/p3a_service_unittest.cc
@@ -91,6 +91,7 @@ class P3AServiceTest : public testing::Test {
 
     p3a_service_->DisableStarAttestationForTesting();
     p3a_service_->Init(shared_url_loader_factory_);
+    p3a_service_->remote_config_manager()->LoadRemoteConfig({});
     task_environment_.RunUntilIdle();
   }
 
diff --git a/components/p3a/remote_config_manager.cc b/components/p3a/remote_config_manager.cc
new file mode 100644
index 000000000000..309e8ed6f9c9
--- /dev/null
+++ b/components/p3a/remote_config_manager.cc
@@ -0,0 +1,164 @@
+/* Copyright (c) 2025 The Brave Authors. All rights reserved.
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at https://mozilla.org/MPL/2.0/. */
+
+#include "brave/components/p3a/remote_config_manager.h"
+
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "base/containers/flat_map.h"
+#include "base/containers/map_util.h"
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/json/json_reader.h"
+#include "base/json/json_value_converter.h"
+#include "base/logging.h"
+#include "base/task/thread_pool.h"
+#include "base/values.h"
+#include "brave/components/p3a/metric_config.h"
+#include "brave/components/p3a/metric_config_utils.h"
+
+constexpr char kMetricsKey[] = "metrics";
+
+namespace p3a {
+
+namespace {
+
+// Reads and parses the p3a_manifest.json file from disk
+std::unique_ptr<base::flat_map<std::string, RemoteMetricConfig>>
+ReadAndParseJsonRules(const base::FilePath& manifest_file_path) {
+  std::string raw_contents;
+  if (!base::ReadFileToString(manifest_file_path, &raw_contents)) {
+    VLOG(1) << "Failed to read p3a manifest";
+    return nullptr;
+  }
+
+  const auto json_root = base::JSONReader::Read(raw_contents);
+  if (!json_root) {
+    VLOG(1) << "Failed to parse p3a manifest";
+    return nullptr;
+  }
+
+  if (!json_root->is_dict()) {
+    VLOG(1) << "Expected dictionary in p3a manifest";
+    return nullptr;
+  }
+
+  const auto* metrics_dict = json_root->GetDict().FindDict(kMetricsKey);
+  if (!metrics_dict) {
+    VLOG(1) << "No metrics found in p3a manifest";
+    return nullptr;
+  }
+
+  base::JSONValueConverter<RemoteMetricConfig> converter;
+
+  auto remote_metric_configs =
+      std::make_unique<base::flat_map<std::string, RemoteMetricConfig>>();
+
+  for (const auto [metric_name, config_value] : *metrics_dict) {
+    if (!config_value.is_dict()) {
+      VLOG(1) << "Metric config for " << metric_name << " is not a dictionary";
+      continue;
+    }
+
+    if (!converter.Convert(config_value,
+                           &(*remote_metric_configs)[metric_name])) {
+      VLOG(1) << "Failed to convert metric config for " << metric_name;
+      remote_metric_configs->erase(metric_name);
+      continue;
+    }
+  }
+
+  return remote_metric_configs;
+}
+
+}  // namespace
+
+RemoteConfigManager::RemoteConfigManager(Delegate* delegate)
+    : delegate_(delegate) {
+  CHECK(delegate_);
+}
+
+RemoteConfigManager::~RemoteConfigManager() = default;
+
+void RemoteConfigManager::LoadRemoteConfig(const base::FilePath& install_dir) {
+  base::FilePath manifest_file_path = install_dir.Append(kP3AManifestFileName);
+
+  base::ThreadPool::PostTaskAndReplyWithResult(
+      FROM_HERE, {base::MayBlock()},
+      base::BindOnce(&ReadAndParseJsonRules, manifest_file_path),
+      base::BindOnce(&RemoteConfigManager::SetMetricConfigs,
+                     weak_factory_.GetWeakPtr()));
+}
+
+void RemoteConfigManager::SetMetricConfigs(
+    std::unique_ptr<base::flat_map<std::string, RemoteMetricConfig>> result) {
+  if (!result) {
+    is_loaded_ = true;
+    delegate_->OnRemoteConfigLoaded();
+    return;
+  }
+
+  VLOG(1) << "Loaded " << result->size() << " metric configurations";
+
+  activation_metric_names_.clear();
+  metric_configs_.clear();
+
+  for (const auto& entry : *result) {
+    if (!delegate_->GetLogTypeForHistogram(entry.first)) {
+      continue;
+    }
+
+    const auto* base_config = delegate_->GetBaseMetricConfig(entry.first);
+    const auto& remote_config = entry.second;
+    auto metric_config = base_config ? *base_config : MetricConfig{};
+
+    metric_config.ephemeral =
+        remote_config.ephemeral.value_or(metric_config.ephemeral);
+    metric_config.constellation_only =
+        remote_config.constellation_only.value_or(
+            metric_config.constellation_only);
+    metric_config.nebula = remote_config.nebula.value_or(metric_config.nebula);
+    metric_config.disable_country_strip =
+        remote_config.disable_country_strip.value_or(
+            metric_config.disable_country_strip);
+    metric_config.record_activation_date =
+        remote_config.record_activation_date.value_or(
+            metric_config.record_activation_date);
+
+    if (remote_config.attributes) {
+      metric_config.attributes = remote_config.attributes;
+    }
+    if (remote_config.append_attributes) {
+      metric_config.append_attributes = *remote_config.append_attributes;
+    }
+    if (remote_config.activation_metric_name) {
+      auto it =
+          activation_metric_names_.insert(*remote_config.activation_metric_name)
+              .first;
+      metric_config.activation_metric_name = *it;
+    }
+    if (remote_config.cadence) {
+      metric_config.cadence = remote_config.cadence;
+    }
+
+    metric_configs_.emplace(entry.first, metric_config);
+  }
+
+  is_loaded_ = true;
+  delegate_->OnRemoteConfigLoaded();
+}
+
+const MetricConfig* RemoteConfigManager::GetRemoteMetricConfig(
+    std::string_view metric_name) const {
+  return base::FindOrNull(metric_configs_, metric_name);
+}
+
+base::WeakPtr<RemoteConfigManager> RemoteConfigManager::GetWeakPtr() {
+  return weak_factory_.GetWeakPtr();
+}
+
+}  // namespace p3a
diff --git a/components/p3a/remote_config_manager.h b/components/p3a/remote_config_manager.h
new file mode 100644
index 000000000000..1e0d330e2ac2
--- /dev/null
+++ b/components/p3a/remote_config_manager.h
@@ -0,0 +1,67 @@
+/* Copyright (c) 2025 The Brave Authors. All rights reserved.
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at https://mozilla.org/MPL/2.0/. */
+
+#ifndef BRAVE_COMPONENTS_P3A_REMOTE_CONFIG_MANAGER_H_
+#define BRAVE_COMPONENTS_P3A_REMOTE_CONFIG_MANAGER_H_
+
+#include <memory>
+#include <string>
+
+#include "base/containers/flat_map.h"
+#include "base/containers/flat_set.h"
+#include "base/files/file_path.h"
+#include "base/memory/raw_ptr.h"
+#include "base/memory/weak_ptr.h"
+#include "brave/components/p3a/metric_config.h"
+#include "brave/components/p3a/metric_log_type.h"
+
+namespace p3a {
+
+inline constexpr base::FilePath::CharType kP3AManifestFileName[] =
+    FILE_PATH_LITERAL("p3a_manifest.json");
+
+// The RemoteConfigManager loads remote configuration data from the Brave Local
+// Data component and provides this configuration as needed.
+class RemoteConfigManager {
+ public:
+  class Delegate {
+   public:
+    virtual ~Delegate() = default;
+
+    virtual std::optional<MetricLogType> GetLogTypeForHistogram(
+        std::string_view histogram_name) const = 0;
+    virtual const MetricConfig* GetBaseMetricConfig(
+        std::string_view histogram_name) const = 0;
+    virtual void OnRemoteConfigLoaded() = 0;
+  };
+
+  explicit RemoteConfigManager(Delegate* delegate);
+  ~RemoteConfigManager();
+
+  void LoadRemoteConfig(const base::FilePath& install_dir);
+
+  const MetricConfig* GetRemoteMetricConfig(std::string_view metric_name) const;
+
+  base::WeakPtr<RemoteConfigManager> GetWeakPtr();
+
+  bool is_loaded() const { return is_loaded_; }
+
+ private:
+  void SetMetricConfigs(
+      std::unique_ptr<base::flat_map<std::string, RemoteMetricConfig>> result);
+
+  base::flat_map<std::string, MetricConfig> metric_configs_;
+  base::flat_set<std::string> activation_metric_names_;
+
+  bool is_loaded_ = false;
+
+  raw_ptr<Delegate> delegate_ = nullptr;
+
+  base::WeakPtrFactory<RemoteConfigManager> weak_factory_{this};
+};
+
+}  // namespace p3a
+
+#endif  // BRAVE_COMPONENTS_P3A_REMOTE_CONFIG_MANAGER_H_
diff --git a/components/p3a/remote_config_manager_unittest.cc b/components/p3a/remote_config_manager_unittest.cc
new file mode 100644
index 000000000000..e8102bb00ae1
--- /dev/null
+++ b/components/p3a/remote_config_manager_unittest.cc
@@ -0,0 +1,179 @@
+/* Copyright (c) 2025 The Brave Authors. All rights reserved.
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at https://mozilla.org/MPL/2.0/. */
+
+#include "brave/components/p3a/remote_config_manager.h"
+
+#include <memory>
+#include <string_view>
+
+#include "base/containers/fixed_flat_map.h"
+#include "base/files/file_path.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/path_service.h"
+#include "base/test/task_environment.h"
+#include "brave/components/constants/brave_paths.h"
+#include "brave/components/p3a/metric_config.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace p3a {
+
+namespace {
+
+constexpr auto kBaseConfigs =
+    base::MakeFixedFlatMap<std::string_view, MetricConfig>({
+        {"Brave.Uptime.BrowserOpenMinutes",
+         MetricConfig{
+             .constellation_only = true,
+             .append_attributes =
+                 MetricAttributesToAppend{MetricAttribute::kDateOfInstall},
+         }},
+        {"Brave.Shields.UsageStatus",
+         MetricConfig{
+             .ephemeral = true,
+             .nebula = true,
+             .disable_country_strip = true,
+             .record_activation_date = true,
+         }},
+        {"Brave.Rewards.WalletBalance",
+         MetricConfig{
+             .attributes = MetricAttributes{MetricAttribute::kVersion,
+                                            MetricAttribute::kChannel},
+         }},
+    });
+
+constexpr auto kLogTypes =
+    base::MakeFixedFlatMap<std::string_view, MetricLogType>({
+        {"Brave.Uptime.BrowserOpenMinutes", MetricLogType::kExpress},
+        {"Brave.Shields.UsageStatus", MetricLogType::kTypical},
+        {"Brave.Rewards.WalletBalance", MetricLogType::kSlow},
+        {"Brave.UnmodifiedMetric", MetricLogType::kTypical},
+    });
+
+}  // namespace
+
+class P3ARemoteConfigManagerTest : public testing::Test,
+                                   public RemoteConfigManager::Delegate {
+ public:
+  P3ARemoteConfigManagerTest()
+      : task_environment_(base::test::TaskEnvironment::TimeSource::MOCK_TIME) {}
+
+  std::optional<MetricLogType> GetLogTypeForHistogram(
+      std::string_view histogram_name) const override {
+    auto it = kLogTypes.find(histogram_name);
+    if (it == kLogTypes.end()) {
+      return std::nullopt;
+    }
+    return it->second;
+  }
+
+  const MetricConfig* GetBaseMetricConfig(
+      std::string_view histogram_name) const override {
+    auto it = kBaseConfigs.find(histogram_name);
+    if (it == kBaseConfigs.end()) {
+      return nullptr;
+    }
+    return &it->second;
+  }
+
+  void OnRemoteConfigLoaded() override { run_loop_.Quit(); }
+
+  void SetUp() override {
+    ASSERT_TRUE(bad_dir_.CreateUniqueTempDir());
+
+    test_data_path_ =
+        base::PathService::CheckedGet(brave::DIR_TEST_DATA).AppendASCII("p3a");
+
+    remote_config_manager_ = std::make_unique<RemoteConfigManager>(this);
+  }
+
+  void TearDown() override { remote_config_manager_.reset(); }
+
+ protected:
+  base::test::TaskEnvironment task_environment_;
+  base::FilePath test_data_path_;
+  base::ScopedTempDir bad_dir_;
+  std::unique_ptr<RemoteConfigManager> remote_config_manager_;
+  base::RunLoop run_loop_;
+};
+
+TEST_F(P3ARemoteConfigManagerTest, LoadRemoteConfig) {
+  remote_config_manager_->LoadRemoteConfig(test_data_path_);
+  run_loop_.Run();
+
+  const auto* uptime_config = remote_config_manager_->GetRemoteMetricConfig(
+      "Brave.Uptime.BrowserOpenMinutes");
+  ASSERT_TRUE(uptime_config);
+  EXPECT_TRUE(uptime_config->ephemeral);
+  EXPECT_TRUE(uptime_config->nebula);
+  EXPECT_FALSE(uptime_config->constellation_only);
+  EXPECT_TRUE(uptime_config->disable_country_strip);
+  EXPECT_TRUE(uptime_config->record_activation_date);
+  ASSERT_TRUE(uptime_config->attributes.has_value());
+  EXPECT_EQ(
+      (*uptime_config->attributes)[0].value_or(MetricAttribute::kMaxValue),
+      MetricAttribute::kVersion);
+  EXPECT_EQ(
+      (*uptime_config->attributes)[1].value_or(MetricAttribute::kMaxValue),
+      MetricAttribute::kPlatform);
+  EXPECT_EQ(
+      uptime_config->append_attributes[0].value_or(MetricAttribute::kMaxValue),
+      MetricAttribute::kDateOfInstall);
+  ASSERT_TRUE(uptime_config->activation_metric_name.has_value());
+  EXPECT_EQ(uptime_config->activation_metric_name.value(),
+            "Brave.Core.LastUsage");
+  ASSERT_TRUE(uptime_config->cadence.has_value());
+  EXPECT_EQ(uptime_config->cadence.value(), MetricLogType::kExpress);
+
+  const auto* shields_config = remote_config_manager_->GetRemoteMetricConfig(
+      "Brave.Shields.UsageStatus");
+  ASSERT_TRUE(shields_config);
+  EXPECT_FALSE(shields_config->ephemeral);
+  EXPECT_FALSE(shields_config->nebula);
+  EXPECT_TRUE(shields_config->constellation_only);
+  EXPECT_FALSE(shields_config->disable_country_strip);
+  EXPECT_FALSE(shields_config->record_activation_date);
+  ASSERT_TRUE(shields_config->cadence.has_value());
+  EXPECT_EQ(shields_config->cadence.value(), MetricLogType::kTypical);
+
+  const auto* rewards_config = remote_config_manager_->GetRemoteMetricConfig(
+      "Brave.Rewards.WalletBalance");
+  ASSERT_TRUE(rewards_config);
+  EXPECT_FALSE(rewards_config->ephemeral);
+  EXPECT_FALSE(rewards_config->nebula);
+  EXPECT_FALSE(rewards_config->constellation_only);
+  ASSERT_TRUE(rewards_config->attributes.has_value());
+  EXPECT_EQ(
+      (*rewards_config->attributes)[0].value_or(MetricAttribute::kMaxValue),
+      MetricAttribute::kAnswerIndex);
+  EXPECT_EQ(
+      (*rewards_config->attributes)[1].value_or(MetricAttribute::kMaxValue),
+      MetricAttribute::kChannel);
+  EXPECT_EQ(
+      (*rewards_config->attributes)[2].value_or(MetricAttribute::kMaxValue),
+      MetricAttribute::kDateOfInstall);
+
+  const auto* unmodified_config =
+      remote_config_manager_->GetRemoteMetricConfig("Brave.UnmodifiedMetric");
+  EXPECT_FALSE(unmodified_config);
+
+  EXPECT_FALSE(
+      remote_config_manager_->GetRemoteMetricConfig("Brave.NonExistentMetric"));
+}
+
+TEST_F(P3ARemoteConfigManagerTest, InvalidConfig) {
+  // Don't set up the test data, which will result in an empty config
+  remote_config_manager_->LoadRemoteConfig(bad_dir_.GetPath());
+  run_loop_.Run();
+
+  // All calls to GetRemoteMetricConfig should return nullptr
+  EXPECT_FALSE(remote_config_manager_->GetRemoteMetricConfig(
+      "Brave.Uptime.BrowserOpenMinutes"));
+  EXPECT_FALSE(remote_config_manager_->GetRemoteMetricConfig(
+      "Brave.Shields.UsageStatus"));
+  EXPECT_FALSE(remote_config_manager_->GetRemoteMetricConfig(
+      "Brave.Rewards.WalletBalance"));
+}
+
+}  // namespace p3a
diff --git a/test/data/p3a/p3a_manifest.json b/test/data/p3a/p3a_manifest.json
new file mode 100644
index 000000000000..26b9d1894a8a
--- /dev/null
+++ b/test/data/p3a/p3a_manifest.json
@@ -0,0 +1,36 @@
+{
+  "metrics": {
+    "Brave.Uptime.BrowserOpenMinutes": {
+      "ephemeral": true,
+      "nebula": true,
+      "constellation_only": false,
+      "disable_country_strip": true,
+      "record_activation_date": true,
+      "attributes": [
+        "version",
+        "platform"
+      ],
+      "append_attributes": [
+        "dtoi"
+      ],
+      "activation_metric_name": "Brave.Core.LastUsage",
+      "cadence": "express"
+    },
+    "Brave.Shields.UsageStatus": {
+      "ephemeral": false,
+      "nebula": false,
+      "constellation_only": true,
+      "disable_country_strip": false,
+      "record_activation_date": false,
+      "cadence": "typical"
+    },
+    "Brave.Rewards.WalletBalance": {
+      "attributes": [
+        "answer_index",
+        "channel",
+        "dtoi"
+      ]
+    },
+    "Brave.NonExistentMetric": {}
+  }
+} 
\ No newline at end of file


Reviews:
user: login: kylehickinson
state: CHANGES_REQUESTED
author_association: COLLABORATOR
submitted_at: 2025-03-28T14:20:42Z
user: login: DJAndries
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-03-28T21:59:17Z
user: login: kylehickinson
state: APPROVED
author_association: COLLABORATOR
submitted_at: 2025-04-09T14:55:35Z
user: login: iefremov
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-07T15:45:51Z
user: login: iefremov
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-07T15:46:08Z
user: login: iefremov
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-07T15:53:12Z
user: login: DJAndries
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-08T17:51:19Z
user: login: DJAndries
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-08T17:54:03Z
user: login: iefremov
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-13T20:31:47Z
user: login: iefremov
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-13T20:32:26Z
user: login: iefremov
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-13T20:33:41Z
user: login: iefremov
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-13T20:34:36Z
user: login: iefremov
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-13T20:43:31Z
user: login: iefremov
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-13T20:56:22Z
user: login: iefremov
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-13T21:07:07Z
user: login: iefremov
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-13T21:07:49Z
user: login: iefremov
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-13T21:08:58Z
user: login: iefremov
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-13T21:09:22Z
user: login: iefremov
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-13T21:09:56Z
user: login: DJAndries
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-13T21:51:28Z
user: login: DJAndries
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-13T22:33:57Z
user: login: DJAndries
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-13T22:34:07Z
user: login: iefremov
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-14T10:09:23Z
user: login: iefremov
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-14T10:11:17Z
user: login: DJAndries
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-14T21:57:19Z
user: login: DJAndries
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-14T22:49:48Z
user: login: iefremov
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-05-27T20:15:21Z

Review comments:
diff_hunk: @@ -30,6 +32,10 @@ void RegisterComponentsForUpdate(
           cus, GetApplicationContext()->GetLocalState());
   ai_chat::ManageLocalModelsComponentRegistration(cus);
   brave_user_agent::RegisterBraveUserAgentComponent(cus);
+  auto* p3a_service = g_brave_browser_process->p3a_service();
path: ios/browser/web/brave_web_main_parts.mm
user: login: kylehickinson
body: This wont compile, as it doesn't exist. Right now the p3a service is only stored in `BraveCoreMain` so you'll have to either move it into the `BraveApplicationContext` (and change how its accessed currently) or pass in the p3a service to `RegisterComponentsForUpdate`
created_at: 2025-03-28T14:20:39Z
author_association: COLLABORATOR
diff_hunk: @@ -30,6 +32,10 @@ void RegisterComponentsForUpdate(
           cus, GetApplicationContext()->GetLocalState());
   ai_chat::ManageLocalModelsComponentRegistration(cus);
   brave_user_agent::RegisterBraveUserAgentComponent(cus);
+  auto* p3a_service = g_brave_browser_process->p3a_service();
path: ios/browser/web/brave_web_main_parts.mm
user: login: DJAndries
body: Since I don't have access to an iOS toolchain, I removed the iOS changes and raised https://github.com/brave/brave-browser/issues/45042 as a follow up.
created_at: 2025-03-28T21:59:16Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,112 @@
+/* Copyright (c) 2025 The Brave Authors. All rights reserved.
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at https://mozilla.org/MPL/2.0/. */
+
+#include "brave/components/p3a/component_installer.h"
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "brave/components/brave_component_updater/browser/brave_on_demand_updater.h"
+#include "brave/components/p3a/remote_config_manager.h"
+#include "components/component_updater/component_installer.h"
+#include "components/component_updater/component_updater_service.h"
+
+using component_updater::ComponentUpdateService;
+
+namespace p3a {
+
+namespace {
+
+constexpr char kP3AComponentName[] = "P3A Configuration";
+constexpr char kP3AComponentId[] = "memmkfnkoigleebghlpgeiecaddoblcl";
+constexpr base::FilePath::CharType kComponentInstallDir[] =
+    FILE_PATH_LITERAL("P3AConfig");
+// public key hash:
+// c4cca5dae86b44167bf64842033e1b2b5c908bd6abc30e32d08b12bf50b2cc03
+const uint8_t kP3AComponentHash[32] = {
+    0xc4, 0xcc, 0xa5, 0xda, 0xe8, 0x6b, 0x44, 0x16, 0x7b, 0xf6, 0x48,
+    0x42, 0x03, 0x3e, 0x1b, 0x2b, 0x5c, 0x90, 0x8b, 0xd6, 0xab, 0xc3,
+    0x0e, 0x32, 0xd0, 0x8b, 0x12, 0xbf, 0x50, 0xb2, 0xcc, 0x03};
+
+class P3AComponentInstallerPolicy
+    : public component_updater::ComponentInstallerPolicy {
+ public:
+  explicit P3AComponentInstallerPolicy(
+      base::WeakPtr<RemoteConfigManager> remote_config_manager)
+      : remote_config_manager_(remote_config_manager) {}
+  ~P3AComponentInstallerPolicy() override = default;
+
+  P3AComponentInstallerPolicy(const P3AComponentInstallerPolicy&) = delete;
+  P3AComponentInstallerPolicy& operator=(const P3AComponentInstallerPolicy&) =
+      delete;
+
+ private:
+  // component_updater::ComponentInstallerPolicy:
+  bool SupportsGroupPolicyEnabledComponentUpdates() const override {
+    return false;
+  }
+
+  bool RequiresNetworkEncryption() const override { return false; }
+
+  update_client::CrxInstaller::Result OnCustomInstall(
+      const base::Value::Dict& manifest,
+      const base::FilePath& install_dir) override {
+    return update_client::CrxInstaller::Result(0);
+  }
+
+  void OnCustomUninstall() override {}
+
+  bool VerifyInstallation(const base::Value::Dict& manifest,
+                          const base::FilePath& install_dir) const override {
+    return base::PathExists(install_dir.Append(kP3AManifestFileName));
+  }
+
+  void ComponentReady(const base::Version& version,
+                      const base::FilePath& install_dir,
+                      base::Value::Dict manifest) override {
+    remote_config_manager_->OnComponentReady(install_dir);
+  }
+
+  base::FilePath GetRelativeInstallDir() const override {
+    return base::FilePath(kComponentInstallDir);
+  }
+
+  void GetHash(std::vector<uint8_t>* hash) const override {
+    hash->assign(std::begin(kP3AComponentHash), std::end(kP3AComponentHash));
+  }
+
+  std::string GetName() const override { return kP3AComponentName; }
+
+  update_client::InstallerAttributes GetInstallerAttributes() const override {
+    return update_client::InstallerAttributes();
+  }
+
+  bool IsBraveComponent() const override { return true; }
+
+  base::WeakPtr<RemoteConfigManager> remote_config_manager_;
path: components/p3a/component_installer.cc
user: login: iefremov
body: why do we store a weak pointer but never check it? perhaps we shouldn't just instantiate the policy without alive config manager?
created_at: 2025-05-07T15:45:51Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,112 @@
+/* Copyright (c) 2025 The Brave Authors. All rights reserved.
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at https://mozilla.org/MPL/2.0/. */
+
+#include "brave/components/p3a/component_installer.h"
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "brave/components/brave_component_updater/browser/brave_on_demand_updater.h"
+#include "brave/components/p3a/remote_config_manager.h"
+#include "components/component_updater/component_installer.h"
+#include "components/component_updater/component_updater_service.h"
+
+using component_updater::ComponentUpdateService;
+
+namespace p3a {
+
+namespace {
+
+constexpr char kP3AComponentName[] = "P3A Configuration";
+constexpr char kP3AComponentId[] = "memmkfnkoigleebghlpgeiecaddoblcl";
+constexpr base::FilePath::CharType kComponentInstallDir[] =
+    FILE_PATH_LITERAL("P3AConfig");
+// public key hash:
+// c4cca5dae86b44167bf64842033e1b2b5c908bd6abc30e32d08b12bf50b2cc03
+const uint8_t kP3AComponentHash[32] = {
+    0xc4, 0xcc, 0xa5, 0xda, 0xe8, 0x6b, 0x44, 0x16, 0x7b, 0xf6, 0x48,
+    0x42, 0x03, 0x3e, 0x1b, 0x2b, 0x5c, 0x90, 0x8b, 0xd6, 0xab, 0xc3,
+    0x0e, 0x32, 0xd0, 0x8b, 0x12, 0xbf, 0x50, 0xb2, 0xcc, 0x03};
+
+class P3AComponentInstallerPolicy
+    : public component_updater::ComponentInstallerPolicy {
+ public:
+  explicit P3AComponentInstallerPolicy(
+      base::WeakPtr<RemoteConfigManager> remote_config_manager)
+      : remote_config_manager_(remote_config_manager) {}
+  ~P3AComponentInstallerPolicy() override = default;
+
+  P3AComponentInstallerPolicy(const P3AComponentInstallerPolicy&) = delete;
+  P3AComponentInstallerPolicy& operator=(const P3AComponentInstallerPolicy&) =
+      delete;
+
+ private:
+  // component_updater::ComponentInstallerPolicy:
+  bool SupportsGroupPolicyEnabledComponentUpdates() const override {
+    return false;
+  }
+
+  bool RequiresNetworkEncryption() const override { return false; }
+
+  update_client::CrxInstaller::Result OnCustomInstall(
+      const base::Value::Dict& manifest,
+      const base::FilePath& install_dir) override {
+    return update_client::CrxInstaller::Result(0);
+  }
+
+  void OnCustomUninstall() override {}
+
+  bool VerifyInstallation(const base::Value::Dict& manifest,
+                          const base::FilePath& install_dir) const override {
+    return base::PathExists(install_dir.Append(kP3AManifestFileName));
+  }
+
+  void ComponentReady(const base::Version& version,
+                      const base::FilePath& install_dir,
+                      base::Value::Dict manifest) override {
+    remote_config_manager_->OnComponentReady(install_dir);
+  }
+
+  base::FilePath GetRelativeInstallDir() const override {
+    return base::FilePath(kComponentInstallDir);
+  }
+
+  void GetHash(std::vector<uint8_t>* hash) const override {
+    hash->assign(std::begin(kP3AComponentHash), std::end(kP3AComponentHash));
+  }
+
+  std::string GetName() const override { return kP3AComponentName; }
+
+  update_client::InstallerAttributes GetInstallerAttributes() const override {
+    return update_client::InstallerAttributes();
+  }
+
+  bool IsBraveComponent() const override { return true; }
+
+  base::WeakPtr<RemoteConfigManager> remote_config_manager_;
+};
+
+}  // namespace
+
+void RegisterP3AComponent(
+    ComponentUpdateService* cus,
+    base::WeakPtr<RemoteConfigManager> remote_config_manager) {
path: components/p3a/component_installer.cc
user: login: iefremov
body: not sure if we need a weak pointer at all
created_at: 2025-05-07T15:46:08Z
author_association: CONTRIBUTOR
diff_hunk: @@ -30,6 +31,10 @@ namespace network {
 class SharedURLLoaderFactory;
 }
 
+namespace brave_component_updater {
+class LocalDataFilesService;
path: components/p3a/p3a_service.h
user: login: iefremov
body: not used?
created_at: 2025-05-07T15:53:12Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,112 @@
+/* Copyright (c) 2025 The Brave Authors. All rights reserved.
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at https://mozilla.org/MPL/2.0/. */
+
+#include "brave/components/p3a/component_installer.h"
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "brave/components/brave_component_updater/browser/brave_on_demand_updater.h"
+#include "brave/components/p3a/remote_config_manager.h"
+#include "components/component_updater/component_installer.h"
+#include "components/component_updater/component_updater_service.h"
+
+using component_updater::ComponentUpdateService;
+
+namespace p3a {
+
+namespace {
+
+constexpr char kP3AComponentName[] = "P3A Configuration";
+constexpr char kP3AComponentId[] = "memmkfnkoigleebghlpgeiecaddoblcl";
+constexpr base::FilePath::CharType kComponentInstallDir[] =
+    FILE_PATH_LITERAL("P3AConfig");
+// public key hash:
+// c4cca5dae86b44167bf64842033e1b2b5c908bd6abc30e32d08b12bf50b2cc03
+const uint8_t kP3AComponentHash[32] = {
+    0xc4, 0xcc, 0xa5, 0xda, 0xe8, 0x6b, 0x44, 0x16, 0x7b, 0xf6, 0x48,
+    0x42, 0x03, 0x3e, 0x1b, 0x2b, 0x5c, 0x90, 0x8b, 0xd6, 0xab, 0xc3,
+    0x0e, 0x32, 0xd0, 0x8b, 0x12, 0xbf, 0x50, 0xb2, 0xcc, 0x03};
+
+class P3AComponentInstallerPolicy
+    : public component_updater::ComponentInstallerPolicy {
+ public:
+  explicit P3AComponentInstallerPolicy(
+      base::WeakPtr<RemoteConfigManager> remote_config_manager)
+      : remote_config_manager_(remote_config_manager) {}
+  ~P3AComponentInstallerPolicy() override = default;
+
+  P3AComponentInstallerPolicy(const P3AComponentInstallerPolicy&) = delete;
+  P3AComponentInstallerPolicy& operator=(const P3AComponentInstallerPolicy&) =
+      delete;
+
+ private:
+  // component_updater::ComponentInstallerPolicy:
+  bool SupportsGroupPolicyEnabledComponentUpdates() const override {
+    return false;
+  }
+
+  bool RequiresNetworkEncryption() const override { return false; }
+
+  update_client::CrxInstaller::Result OnCustomInstall(
+      const base::Value::Dict& manifest,
+      const base::FilePath& install_dir) override {
+    return update_client::CrxInstaller::Result(0);
+  }
+
+  void OnCustomUninstall() override {}
+
+  bool VerifyInstallation(const base::Value::Dict& manifest,
+                          const base::FilePath& install_dir) const override {
+    return base::PathExists(install_dir.Append(kP3AManifestFileName));
+  }
+
+  void ComponentReady(const base::Version& version,
+                      const base::FilePath& install_dir,
+                      base::Value::Dict manifest) override {
+    remote_config_manager_->OnComponentReady(install_dir);
+  }
+
+  base::FilePath GetRelativeInstallDir() const override {
+    return base::FilePath(kComponentInstallDir);
+  }
+
+  void GetHash(std::vector<uint8_t>* hash) const override {
+    hash->assign(std::begin(kP3AComponentHash), std::end(kP3AComponentHash));
+  }
+
+  std::string GetName() const override { return kP3AComponentName; }
+
+  update_client::InstallerAttributes GetInstallerAttributes() const override {
+    return update_client::InstallerAttributes();
+  }
+
+  bool IsBraveComponent() const override { return true; }
+
+  base::WeakPtr<RemoteConfigManager> remote_config_manager_;
path: components/p3a/component_installer.cc
user: login: DJAndries
body: Are you asking why we don't check the pointer for validity, or why we don't use the pointer? We check the pointer in `RegisterP3AComponent`, and use the pointer in `ComponentReady`.
created_at: 2025-05-08T17:51:19Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,112 @@
+/* Copyright (c) 2025 The Brave Authors. All rights reserved.
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at https://mozilla.org/MPL/2.0/. */
+
+#include "brave/components/p3a/component_installer.h"
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "brave/components/brave_component_updater/browser/brave_on_demand_updater.h"
+#include "brave/components/p3a/remote_config_manager.h"
+#include "components/component_updater/component_installer.h"
+#include "components/component_updater/component_updater_service.h"
+
+using component_updater::ComponentUpdateService;
+
+namespace p3a {
+
+namespace {
+
+constexpr char kP3AComponentName[] = "P3A Configuration";
+constexpr char kP3AComponentId[] = "memmkfnkoigleebghlpgeiecaddoblcl";
+constexpr base::FilePath::CharType kComponentInstallDir[] =
+    FILE_PATH_LITERAL("P3AConfig");
+// public key hash:
+// c4cca5dae86b44167bf64842033e1b2b5c908bd6abc30e32d08b12bf50b2cc03
+const uint8_t kP3AComponentHash[32] = {
+    0xc4, 0xcc, 0xa5, 0xda, 0xe8, 0x6b, 0x44, 0x16, 0x7b, 0xf6, 0x48,
+    0x42, 0x03, 0x3e, 0x1b, 0x2b, 0x5c, 0x90, 0x8b, 0xd6, 0xab, 0xc3,
+    0x0e, 0x32, 0xd0, 0x8b, 0x12, 0xbf, 0x50, 0xb2, 0xcc, 0x03};
+
+class P3AComponentInstallerPolicy
+    : public component_updater::ComponentInstallerPolicy {
+ public:
+  explicit P3AComponentInstallerPolicy(
+      base::WeakPtr<RemoteConfigManager> remote_config_manager)
+      : remote_config_manager_(remote_config_manager) {}
+  ~P3AComponentInstallerPolicy() override = default;
+
+  P3AComponentInstallerPolicy(const P3AComponentInstallerPolicy&) = delete;
+  P3AComponentInstallerPolicy& operator=(const P3AComponentInstallerPolicy&) =
+      delete;
+
+ private:
+  // component_updater::ComponentInstallerPolicy:
+  bool SupportsGroupPolicyEnabledComponentUpdates() const override {
+    return false;
+  }
+
+  bool RequiresNetworkEncryption() const override { return false; }
+
+  update_client::CrxInstaller::Result OnCustomInstall(
+      const base::Value::Dict& manifest,
+      const base::FilePath& install_dir) override {
+    return update_client::CrxInstaller::Result(0);
+  }
+
+  void OnCustomUninstall() override {}
+
+  bool VerifyInstallation(const base::Value::Dict& manifest,
+                          const base::FilePath& install_dir) const override {
+    return base::PathExists(install_dir.Append(kP3AManifestFileName));
+  }
+
+  void ComponentReady(const base::Version& version,
+                      const base::FilePath& install_dir,
+                      base::Value::Dict manifest) override {
+    remote_config_manager_->OnComponentReady(install_dir);
+  }
+
+  base::FilePath GetRelativeInstallDir() const override {
+    return base::FilePath(kComponentInstallDir);
+  }
+
+  void GetHash(std::vector<uint8_t>* hash) const override {
+    hash->assign(std::begin(kP3AComponentHash), std::end(kP3AComponentHash));
+  }
+
+  std::string GetName() const override { return kP3AComponentName; }
+
+  update_client::InstallerAttributes GetInstallerAttributes() const override {
+    return update_client::InstallerAttributes();
+  }
+
+  bool IsBraveComponent() const override { return true; }
+
+  base::WeakPtr<RemoteConfigManager> remote_config_manager_;
+};
+
+}  // namespace
+
+void RegisterP3AComponent(
+    ComponentUpdateService* cus,
+    base::WeakPtr<RemoteConfigManager> remote_config_manager) {
path: components/p3a/component_installer.cc
user: login: DJAndries
body: If we were to use a raw pointer, wouldn't we have to guarantee that the P3A service outlives the installer policy?
created_at: 2025-05-08T17:54:03Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,112 @@
+/* Copyright (c) 2025 The Brave Authors. All rights reserved.
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at https://mozilla.org/MPL/2.0/. */
+
+#include "brave/components/p3a/component_installer.h"
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "brave/components/brave_component_updater/browser/brave_on_demand_updater.h"
+#include "brave/components/p3a/remote_config_manager.h"
+#include "components/component_updater/component_installer.h"
+#include "components/component_updater/component_updater_service.h"
+
+using component_updater::ComponentUpdateService;
+
+namespace p3a {
+
+namespace {
+
+constexpr char kP3AComponentName[] = "P3A Configuration";
+constexpr char kP3AComponentId[] = "memmkfnkoigleebghlpgeiecaddoblcl";
+constexpr base::FilePath::CharType kComponentInstallDir[] =
+    FILE_PATH_LITERAL("P3AConfig");
+// public key hash:
+// c4cca5dae86b44167bf64842033e1b2b5c908bd6abc30e32d08b12bf50b2cc03
+const uint8_t kP3AComponentHash[32] = {
+    0xc4, 0xcc, 0xa5, 0xda, 0xe8, 0x6b, 0x44, 0x16, 0x7b, 0xf6, 0x48,
+    0x42, 0x03, 0x3e, 0x1b, 0x2b, 0x5c, 0x90, 0x8b, 0xd6, 0xab, 0xc3,
+    0x0e, 0x32, 0xd0, 0x8b, 0x12, 0xbf, 0x50, 0xb2, 0xcc, 0x03};
+
+class P3AComponentInstallerPolicy
+    : public component_updater::ComponentInstallerPolicy {
+ public:
+  explicit P3AComponentInstallerPolicy(
+      base::WeakPtr<RemoteConfigManager> remote_config_manager)
+      : remote_config_manager_(remote_config_manager) {}
+  ~P3AComponentInstallerPolicy() override = default;
+
+  P3AComponentInstallerPolicy(const P3AComponentInstallerPolicy&) = delete;
+  P3AComponentInstallerPolicy& operator=(const P3AComponentInstallerPolicy&) =
+      delete;
+
+ private:
+  // component_updater::ComponentInstallerPolicy:
+  bool SupportsGroupPolicyEnabledComponentUpdates() const override {
+    return false;
+  }
+
+  bool RequiresNetworkEncryption() const override { return false; }
+
+  update_client::CrxInstaller::Result OnCustomInstall(
+      const base::Value::Dict& manifest,
+      const base::FilePath& install_dir) override {
+    return update_client::CrxInstaller::Result(0);
+  }
+
+  void OnCustomUninstall() override {}
+
+  bool VerifyInstallation(const base::Value::Dict& manifest,
+                          const base::FilePath& install_dir) const override {
+    return base::PathExists(install_dir.Append(kP3AManifestFileName));
+  }
+
+  void ComponentReady(const base::Version& version,
+                      const base::FilePath& install_dir,
+                      base::Value::Dict manifest) override {
+    remote_config_manager_->OnComponentReady(install_dir);
+  }
+
+  base::FilePath GetRelativeInstallDir() const override {
+    return base::FilePath(kComponentInstallDir);
+  }
+
+  void GetHash(std::vector<uint8_t>* hash) const override {
+    hash->assign(std::begin(kP3AComponentHash), std::end(kP3AComponentHash));
+  }
+
+  std::string GetName() const override { return kP3AComponentName; }
+
+  update_client::InstallerAttributes GetInstallerAttributes() const override {
+    return update_client::InstallerAttributes();
+  }
+
+  bool IsBraveComponent() const override { return true; }
+
+  base::WeakPtr<RemoteConfigManager> remote_config_manager_;
path: components/p3a/component_installer.cc
user: login: iefremov
body: I mean, checking the pointer. If we already ensured that the pointer is non-null when it is passed to `P3AComponentInstallerPolicy` constructor, why to keep the weak pointer at all? In case you don't check it before using in ComponentReady
created_at: 2025-05-13T20:31:47Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,112 @@
+/* Copyright (c) 2025 The Brave Authors. All rights reserved.
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at https://mozilla.org/MPL/2.0/. */
+
+#include "brave/components/p3a/component_installer.h"
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "brave/components/brave_component_updater/browser/brave_on_demand_updater.h"
+#include "brave/components/p3a/remote_config_manager.h"
+#include "components/component_updater/component_installer.h"
+#include "components/component_updater/component_updater_service.h"
+
+using component_updater::ComponentUpdateService;
+
+namespace p3a {
+
+namespace {
+
+constexpr char kP3AComponentName[] = "P3A Configuration";
+constexpr char kP3AComponentId[] = "memmkfnkoigleebghlpgeiecaddoblcl";
+constexpr base::FilePath::CharType kComponentInstallDir[] =
+    FILE_PATH_LITERAL("P3AConfig");
+// public key hash:
+// c4cca5dae86b44167bf64842033e1b2b5c908bd6abc30e32d08b12bf50b2cc03
+const uint8_t kP3AComponentHash[32] = {
+    0xc4, 0xcc, 0xa5, 0xda, 0xe8, 0x6b, 0x44, 0x16, 0x7b, 0xf6, 0x48,
+    0x42, 0x03, 0x3e, 0x1b, 0x2b, 0x5c, 0x90, 0x8b, 0xd6, 0xab, 0xc3,
+    0x0e, 0x32, 0xd0, 0x8b, 0x12, 0xbf, 0x50, 0xb2, 0xcc, 0x03};
+
+class P3AComponentInstallerPolicy
+    : public component_updater::ComponentInstallerPolicy {
+ public:
+  explicit P3AComponentInstallerPolicy(
+      base::WeakPtr<RemoteConfigManager> remote_config_manager)
+      : remote_config_manager_(remote_config_manager) {}
+  ~P3AComponentInstallerPolicy() override = default;
+
+  P3AComponentInstallerPolicy(const P3AComponentInstallerPolicy&) = delete;
+  P3AComponentInstallerPolicy& operator=(const P3AComponentInstallerPolicy&) =
+      delete;
+
+ private:
+  // component_updater::ComponentInstallerPolicy:
+  bool SupportsGroupPolicyEnabledComponentUpdates() const override {
+    return false;
+  }
+
+  bool RequiresNetworkEncryption() const override { return false; }
+
+  update_client::CrxInstaller::Result OnCustomInstall(
+      const base::Value::Dict& manifest,
+      const base::FilePath& install_dir) override {
+    return update_client::CrxInstaller::Result(0);
+  }
+
+  void OnCustomUninstall() override {}
+
+  bool VerifyInstallation(const base::Value::Dict& manifest,
+                          const base::FilePath& install_dir) const override {
+    return base::PathExists(install_dir.Append(kP3AManifestFileName));
+  }
+
+  void ComponentReady(const base::Version& version,
+                      const base::FilePath& install_dir,
+                      base::Value::Dict manifest) override {
+    remote_config_manager_->OnComponentReady(install_dir);
+  }
+
+  base::FilePath GetRelativeInstallDir() const override {
+    return base::FilePath(kComponentInstallDir);
+  }
+
+  void GetHash(std::vector<uint8_t>* hash) const override {
+    hash->assign(std::begin(kP3AComponentHash), std::end(kP3AComponentHash));
+  }
+
+  std::string GetName() const override { return kP3AComponentName; }
+
+  update_client::InstallerAttributes GetInstallerAttributes() const override {
+    return update_client::InstallerAttributes();
+  }
+
+  bool IsBraveComponent() const override { return true; }
+
+  base::WeakPtr<RemoteConfigManager> remote_config_manager_;
path: components/p3a/component_installer.cc
user: login: iefremov
body: Basically it is the thing that you are assumed to check for validity before using, otherwise you probably don't need a weak pointer
created_at: 2025-05-13T20:32:25Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,112 @@
+/* Copyright (c) 2025 The Brave Authors. All rights reserved.
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at https://mozilla.org/MPL/2.0/. */
+
+#include "brave/components/p3a/component_installer.h"
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "brave/components/brave_component_updater/browser/brave_on_demand_updater.h"
+#include "brave/components/p3a/remote_config_manager.h"
+#include "components/component_updater/component_installer.h"
+#include "components/component_updater/component_updater_service.h"
+
+using component_updater::ComponentUpdateService;
+
+namespace p3a {
+
+namespace {
+
+constexpr char kP3AComponentName[] = "P3A Configuration";
+constexpr char kP3AComponentId[] = "memmkfnkoigleebghlpgeiecaddoblcl";
+constexpr base::FilePath::CharType kComponentInstallDir[] =
+    FILE_PATH_LITERAL("P3AConfig");
+// public key hash:
+// c4cca5dae86b44167bf64842033e1b2b5c908bd6abc30e32d08b12bf50b2cc03
+const uint8_t kP3AComponentHash[32] = {
+    0xc4, 0xcc, 0xa5, 0xda, 0xe8, 0x6b, 0x44, 0x16, 0x7b, 0xf6, 0x48,
+    0x42, 0x03, 0x3e, 0x1b, 0x2b, 0x5c, 0x90, 0x8b, 0xd6, 0xab, 0xc3,
+    0x0e, 0x32, 0xd0, 0x8b, 0x12, 0xbf, 0x50, 0xb2, 0xcc, 0x03};
+
+class P3AComponentInstallerPolicy
+    : public component_updater::ComponentInstallerPolicy {
+ public:
+  explicit P3AComponentInstallerPolicy(
+      base::WeakPtr<RemoteConfigManager> remote_config_manager)
+      : remote_config_manager_(remote_config_manager) {}
+  ~P3AComponentInstallerPolicy() override = default;
+
+  P3AComponentInstallerPolicy(const P3AComponentInstallerPolicy&) = delete;
+  P3AComponentInstallerPolicy& operator=(const P3AComponentInstallerPolicy&) =
+      delete;
+
+ private:
+  // component_updater::ComponentInstallerPolicy:
+  bool SupportsGroupPolicyEnabledComponentUpdates() const override {
+    return false;
+  }
+
+  bool RequiresNetworkEncryption() const override { return false; }
+
+  update_client::CrxInstaller::Result OnCustomInstall(
+      const base::Value::Dict& manifest,
+      const base::FilePath& install_dir) override {
+    return update_client::CrxInstaller::Result(0);
+  }
+
+  void OnCustomUninstall() override {}
+
+  bool VerifyInstallation(const base::Value::Dict& manifest,
+                          const base::FilePath& install_dir) const override {
+    return base::PathExists(install_dir.Append(kP3AManifestFileName));
+  }
+
+  void ComponentReady(const base::Version& version,
+                      const base::FilePath& install_dir,
+                      base::Value::Dict manifest) override {
+    remote_config_manager_->OnComponentReady(install_dir);
+  }
+
+  base::FilePath GetRelativeInstallDir() const override {
+    return base::FilePath(kComponentInstallDir);
+  }
+
+  void GetHash(std::vector<uint8_t>* hash) const override {
+    hash->assign(std::begin(kP3AComponentHash), std::end(kP3AComponentHash));
+  }
+
+  std::string GetName() const override { return kP3AComponentName; }
+
+  update_client::InstallerAttributes GetInstallerAttributes() const override {
+    return update_client::InstallerAttributes();
+  }
+
+  bool IsBraveComponent() const override { return true; }
+
+  base::WeakPtr<RemoteConfigManager> remote_config_manager_;
+};
+
+}  // namespace
+
+void RegisterP3AComponent(
+    ComponentUpdateService* cus,
+    base::WeakPtr<RemoteConfigManager> remote_config_manager) {
path: components/p3a/component_installer.cc
user: login: iefremov
body: You need to validate it in ComponentReady if you assume the p3a service can die earlier
created_at: 2025-05-13T20:33:41Z
author_association: CONTRIBUTOR
diff_hunk: @@ -27,6 +30,10 @@ void RegisterComponentsForUpdate() {
       .MaybeRegisterWalletDataFilesComponent(cus,
                                              g_browser_process->local_state());
   psst::RegisterPsstComponent(cus);
+  auto* p3a_service = g_brave_browser_process->p3a_service();
+  p3a::RegisterP3AComponent(
+      cus, p3a_service ? p3a_service->remote_config_manager()->GetWeakPtr()
+                       : nullptr);
path: chromium_src/chrome/browser/component_updater/registration.cc
user: login: iefremov
body: we do early return in `RegisterP3AComponent` in case the config manager is null. Maybe we just shouldn't call the function if it is null?
created_at: 2025-05-13T20:34:36Z
author_association: CONTRIBUTOR
diff_hunk: @@ -63,6 +67,31 @@ struct MetricConfig {
   // If provided, the activation date recorded from another metric
   // will be reported.
   std::optional<std::string_view> activation_metric_name;
+
+  // If specified in a remote configuration, the cadence of the metric will be
+  // overridden.
+  std::optional<MetricLogType> cadence;
+};
+
+struct RemoteMetricConfig {
path: components/p3a/metric_config.h
user: login: iefremov
body: can you add a short comment explaining this is configurable via the component
created_at: 2025-05-13T20:43:31Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,155 @@
+/* Copyright (c) 2025 The Brave Authors. All rights reserved.
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at https://mozilla.org/MPL/2.0/. */
+
+#include "brave/components/p3a/metric_config.h"
+
+#include "base/containers/fixed_flat_map.h"
+
+namespace p3a {
+
+namespace {
+
+constexpr auto kMetricAttributeMap =
+    base::MakeFixedFlatMap<std::string_view, MetricAttribute>({
+        {"answer_index", MetricAttribute::kAnswerIndex},
+        {"version", MetricAttribute::kVersion},
+        {"yoi", MetricAttribute::kYoi},
+        {"channel", MetricAttribute::kChannel},
+        {"platform", MetricAttribute::kPlatform},
+        {"country_code", MetricAttribute::kCountryCode},
+        {"woi", MetricAttribute::kWoi},
+        {"general_platform", MetricAttribute::kGeneralPlatform},
+        {"region", MetricAttribute::kRegion},
+        {"subregion", MetricAttribute::kSubregion},
+        {"ref", MetricAttribute::kRef},
+        {"dtoi", MetricAttribute::kDateOfInstall},
+        {"dtoa", MetricAttribute::kDateOfActivation},
+    });
+
+bool GetMetricAttribute(const base::Value* value,
+                        std::optional<MetricAttribute>* field) {
+  if (!value || !value->is_string()) {
+    return false;
+  }
+
+  const auto& str = value->GetString();
+  auto it = kMetricAttributeMap.find(str);
+  if (it == kMetricAttributeMap.end()) {
+    return false;
+  }
+  *field = it->second;
+  return true;
+}
+
+bool GetMetricLogType(const base::Value* value,
+                      std::optional<MetricLogType>* field) {
+  if (!value || !value->is_string()) {
+    return false;
+  }
+
+  auto log_type = StringToMetricLogType(value->GetString());
+  if (!log_type) {
+    return false;
+  }
+  *field = log_type.value();
+  return true;
+}
+
+bool GetMetricAttributes(const base::Value* value,
+                         std::optional<MetricAttributes>* field) {
+  if (!value || !value->is_list()) {
+    return false;
+  }
+
+  const auto& list = value->GetList();
+
+  MetricAttributes attributes;
+
+  for (size_t i = 0; i < list.size() && i < attributes.size(); i++) {
+    if (!GetMetricAttribute(&list[i], &attributes[i])) {
+      return false;
+    }
+  }
+
+  *field = attributes;
+  return true;
+}
+
+bool GetAppendAttributes(const base::Value* value,
+                         std::optional<MetricAttributesToAppend>* field) {
+  if (!value || !value->is_list()) {
+    return false;
+  }
+
+  const auto& list = value->GetList();
+
+  MetricAttributesToAppend attributes;
+
+  for (size_t i = 0; i < list.size() && i < attributes.size(); i++) {
+    if (!GetMetricAttribute(&list[i], &attributes[i])) {
+      return false;
+    }
+  }
+
+  *field = attributes;
+
+  return true;
+}
+
+bool GetOptionalString(const base::Value* value,
+                       std::optional<std::string>* field) {
+  if (!value || !value->is_string()) {
+    return false;
+  }
+
+  *field = value->GetString();
+  return true;
+}
+
+bool GetOptionalBool(const base::Value* value, std::optional<bool>* field) {
+  if (!value || !value->is_bool()) {
+    return false;
+  }
+  *field = value->GetBool();
+  return true;
+}
+
+}  // namespace
+
+RemoteMetricConfig::RemoteMetricConfig() = default;
+RemoteMetricConfig::~RemoteMetricConfig() = default;
+
+RemoteMetricConfig::RemoteMetricConfig(const RemoteMetricConfig&) = default;
+RemoteMetricConfig& RemoteMetricConfig::operator=(const RemoteMetricConfig&) =
+    default;
+
+void RemoteMetricConfig::RegisterJSONConverter(
+    base::JSONValueConverter<RemoteMetricConfig>* converter) {
+  converter->RegisterCustomValueField(
+      "ephemeral", &RemoteMetricConfig::ephemeral, &GetOptionalBool);
path: components/p3a/metric_config.cc
user: login: iefremov
body: do we really need the optional thing, given that we control the JSON? Perhaps just use default converter methods and fail if something is wrong?
created_at: 2025-05-13T20:56:21Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,165 @@
+/* Copyright (c) 2025 The Brave Authors. All rights reserved.
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at https://mozilla.org/MPL/2.0/. */
+
+#include "brave/components/p3a/remote_config_manager.h"
+
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "base/containers/flat_map.h"
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/json/json_reader.h"
+#include "base/json/json_value_converter.h"
+#include "base/logging.h"
+#include "base/task/thread_pool.h"
+#include "base/values.h"
+#include "brave/components/p3a/metric_config.h"
+
+constexpr char kMetricsKey[] = "metrics";
+
+namespace p3a {
+
+namespace {
+
+// Reads and parses the p3a_manifest.json file from disk
+std::unique_ptr<base::flat_map<std::string, RemoteMetricConfig>>
+ReadAndParseJsonRules(const base::FilePath& manifest_file_path) {
+  std::string raw_contents;
+  if (!base::ReadFileToString(manifest_file_path, &raw_contents)) {
+    VLOG(1) << "Failed to read p3a manifest";
+    return nullptr;
+  }
+
+  const auto json_root = base::JSONReader::Read(raw_contents);
+  if (!json_root) {
+    VLOG(1) << "Failed to parse p3a manifest";
+    return nullptr;
+  }
+
+  if (!json_root->is_dict()) {
+    VLOG(1) << "Expected dictionary in p3a manifest";
+    return nullptr;
+  }
+
+  const auto* metrics_dict = json_root->GetDict().FindDict(kMetricsKey);
+  if (!metrics_dict) {
+    VLOG(1) << "No metrics found in p3a manifest";
+    return nullptr;
+  }
+
+  base::JSONValueConverter<RemoteMetricConfig> converter;
+
+  auto remote_metric_configs =
+      std::make_unique<base::flat_map<std::string, RemoteMetricConfig>>();
+
+  for (const auto [metric_name, config_value] : *metrics_dict) {
+    if (!config_value.is_dict()) {
+      VLOG(1) << "Metric config for " << metric_name << " is not a dictionary";
+      continue;
+    }
+
+    if (!converter.Convert(config_value,
+                           &(*remote_metric_configs)[metric_name])) {
+      VLOG(1) << "Failed to convert metric config for " << metric_name;
+      remote_metric_configs->erase(metric_name);
+      continue;
+    }
+  }
+
+  return remote_metric_configs;
+}
+
+}  // namespace
+
+RemoteConfigManager::RemoteConfigManager(Delegate* delegate)
+    : delegate_(delegate) {}
+
+RemoteConfigManager::~RemoteConfigManager() = default;
+
+void RemoteConfigManager::OnComponentReady(const base::FilePath& install_dir) {
+  LoadRemoteConfig(install_dir);
+}
+
+void RemoteConfigManager::LoadRemoteConfig(const base::FilePath& install_dir) {
+  base::FilePath manifest_file_path =
+      install_dir.AppendASCII(kP3AManifestFileName);
+
+  base::ThreadPool::PostTaskAndReplyWithResult(
+      FROM_HERE, {base::MayBlock()},
+      base::BindOnce(&ReadAndParseJsonRules, manifest_file_path),
+      base::BindOnce(&RemoteConfigManager::SetMetricConfigs,
+                     weak_factory_.GetWeakPtr()));
+}
+
+void RemoteConfigManager::SetMetricConfigs(
+    std::unique_ptr<base::flat_map<std::string, RemoteMetricConfig>> result) {
+  if (!result) {
+    return;
+  }
+
+  VLOG(1) << "Loaded " << result->size() << " metric configurations";
+
+  activation_metric_names_.clear();
+
+  for (const auto& entry : *result) {
+    if (!delegate_->GetLogTypeForHistogram(entry.first)) {
+      continue;
+    }
+
+    const auto* base_config = delegate_->GetBaseMetricConfig(entry.first);
+
+    const auto& remote_config = entry.second;
+
+    auto metric_config = base_config ? *base_config : MetricConfig{};
+
+    metric_config.ephemeral =
+        remote_config.ephemeral.value_or(metric_config.ephemeral);
+    metric_config.constellation_only =
+        remote_config.constellation_only.value_or(
+            metric_config.constellation_only);
+    metric_config.nebula = remote_config.nebula.value_or(metric_config.nebula);
+    metric_config.disable_country_strip =
+        remote_config.disable_country_strip.value_or(
+            metric_config.disable_country_strip);
+    metric_config.record_activation_date =
+        remote_config.record_activation_date.value_or(
+            metric_config.record_activation_date);
+
+    if (remote_config.attributes) {
+      metric_config.attributes = remote_config.attributes;
+    }
+    if (remote_config.append_attributes) {
+      metric_config.append_attributes = *remote_config.append_attributes;
+    }
+    if (remote_config.activation_metric_name) {
+      auto it =
+          activation_metric_names_.insert(*remote_config.activation_metric_name)
+              .first;
+      metric_config.activation_metric_name = *it;
+    }
+    if (remote_config.cadence) {
+      metric_config.cadence = remote_config.cadence;
+    }
+
+    metric_configs_.emplace(entry.first, metric_config);
+  }
+}
+
+const MetricConfig* RemoteConfigManager::GetRemoteMetricConfig(
+    std::string_view metric_name) const {
+  auto it = metric_configs_.find(metric_name);
+  if (it == metric_configs_.end()) {
+    return nullptr;
+  }
+  return &it->second;
path: components/p3a/remote_config_manager.cc
user: login: iefremov
body: could be base::FindOrNull()
created_at: 2025-05-13T21:07:07Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,165 @@
+/* Copyright (c) 2025 The Brave Authors. All rights reserved.
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at https://mozilla.org/MPL/2.0/. */
+
+#include "brave/components/p3a/remote_config_manager.h"
+
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "base/containers/flat_map.h"
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/json/json_reader.h"
+#include "base/json/json_value_converter.h"
+#include "base/logging.h"
+#include "base/task/thread_pool.h"
+#include "base/values.h"
+#include "brave/components/p3a/metric_config.h"
+
+constexpr char kMetricsKey[] = "metrics";
+
+namespace p3a {
+
+namespace {
+
+// Reads and parses the p3a_manifest.json file from disk
+std::unique_ptr<base::flat_map<std::string, RemoteMetricConfig>>
+ReadAndParseJsonRules(const base::FilePath& manifest_file_path) {
+  std::string raw_contents;
+  if (!base::ReadFileToString(manifest_file_path, &raw_contents)) {
+    VLOG(1) << "Failed to read p3a manifest";
+    return nullptr;
+  }
+
+  const auto json_root = base::JSONReader::Read(raw_contents);
+  if (!json_root) {
+    VLOG(1) << "Failed to parse p3a manifest";
+    return nullptr;
+  }
+
+  if (!json_root->is_dict()) {
+    VLOG(1) << "Expected dictionary in p3a manifest";
+    return nullptr;
+  }
+
+  const auto* metrics_dict = json_root->GetDict().FindDict(kMetricsKey);
+  if (!metrics_dict) {
+    VLOG(1) << "No metrics found in p3a manifest";
+    return nullptr;
+  }
+
+  base::JSONValueConverter<RemoteMetricConfig> converter;
+
+  auto remote_metric_configs =
+      std::make_unique<base::flat_map<std::string, RemoteMetricConfig>>();
+
+  for (const auto [metric_name, config_value] : *metrics_dict) {
+    if (!config_value.is_dict()) {
+      VLOG(1) << "Metric config for " << metric_name << " is not a dictionary";
+      continue;
+    }
+
+    if (!converter.Convert(config_value,
+                           &(*remote_metric_configs)[metric_name])) {
+      VLOG(1) << "Failed to convert metric config for " << metric_name;
+      remote_metric_configs->erase(metric_name);
+      continue;
+    }
+  }
+
+  return remote_metric_configs;
+}
+
+}  // namespace
+
+RemoteConfigManager::RemoteConfigManager(Delegate* delegate)
+    : delegate_(delegate) {}
+
+RemoteConfigManager::~RemoteConfigManager() = default;
+
+void RemoteConfigManager::OnComponentReady(const base::FilePath& install_dir) {
+  LoadRemoteConfig(install_dir);
+}
+
+void RemoteConfigManager::LoadRemoteConfig(const base::FilePath& install_dir) {
path: components/p3a/remote_config_manager.cc
user: login: iefremov
body: why have this functionat all? mb just put the code into `OnComponentReady`
created_at: 2025-05-13T21:07:49Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,165 @@
+/* Copyright (c) 2025 The Brave Authors. All rights reserved.
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at https://mozilla.org/MPL/2.0/. */
+
+#include "brave/components/p3a/remote_config_manager.h"
+
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "base/containers/flat_map.h"
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/json/json_reader.h"
+#include "base/json/json_value_converter.h"
+#include "base/logging.h"
+#include "base/task/thread_pool.h"
+#include "base/values.h"
+#include "brave/components/p3a/metric_config.h"
+
+constexpr char kMetricsKey[] = "metrics";
+
+namespace p3a {
+
+namespace {
+
+// Reads and parses the p3a_manifest.json file from disk
+std::unique_ptr<base::flat_map<std::string, RemoteMetricConfig>>
+ReadAndParseJsonRules(const base::FilePath& manifest_file_path) {
+  std::string raw_contents;
+  if (!base::ReadFileToString(manifest_file_path, &raw_contents)) {
+    VLOG(1) << "Failed to read p3a manifest";
+    return nullptr;
+  }
+
+  const auto json_root = base::JSONReader::Read(raw_contents);
+  if (!json_root) {
+    VLOG(1) << "Failed to parse p3a manifest";
+    return nullptr;
+  }
+
+  if (!json_root->is_dict()) {
+    VLOG(1) << "Expected dictionary in p3a manifest";
+    return nullptr;
+  }
+
+  const auto* metrics_dict = json_root->GetDict().FindDict(kMetricsKey);
+  if (!metrics_dict) {
+    VLOG(1) << "No metrics found in p3a manifest";
+    return nullptr;
+  }
+
+  base::JSONValueConverter<RemoteMetricConfig> converter;
+
+  auto remote_metric_configs =
+      std::make_unique<base::flat_map<std::string, RemoteMetricConfig>>();
+
+  for (const auto [metric_name, config_value] : *metrics_dict) {
+    if (!config_value.is_dict()) {
+      VLOG(1) << "Metric config for " << metric_name << " is not a dictionary";
+      continue;
+    }
+
+    if (!converter.Convert(config_value,
+                           &(*remote_metric_configs)[metric_name])) {
+      VLOG(1) << "Failed to convert metric config for " << metric_name;
+      remote_metric_configs->erase(metric_name);
+      continue;
+    }
+  }
+
+  return remote_metric_configs;
+}
+
+}  // namespace
+
+RemoteConfigManager::RemoteConfigManager(Delegate* delegate)
+    : delegate_(delegate) {}
path: components/p3a/remote_config_manager.cc
user: login: iefremov
body: i would CHECK(delegate)
created_at: 2025-05-13T21:08:58Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,165 @@
+/* Copyright (c) 2025 The Brave Authors. All rights reserved.
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at https://mozilla.org/MPL/2.0/. */
+
+#include "brave/components/p3a/remote_config_manager.h"
+
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "base/containers/flat_map.h"
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/json/json_reader.h"
+#include "base/json/json_value_converter.h"
+#include "base/logging.h"
+#include "base/task/thread_pool.h"
+#include "base/values.h"
+#include "brave/components/p3a/metric_config.h"
+
+constexpr char kMetricsKey[] = "metrics";
+
+namespace p3a {
+
+namespace {
+
+// Reads and parses the p3a_manifest.json file from disk
+std::unique_ptr<base::flat_map<std::string, RemoteMetricConfig>>
+ReadAndParseJsonRules(const base::FilePath& manifest_file_path) {
+  std::string raw_contents;
+  if (!base::ReadFileToString(manifest_file_path, &raw_contents)) {
+    VLOG(1) << "Failed to read p3a manifest";
+    return nullptr;
+  }
+
+  const auto json_root = base::JSONReader::Read(raw_contents);
+  if (!json_root) {
+    VLOG(1) << "Failed to parse p3a manifest";
+    return nullptr;
+  }
+
+  if (!json_root->is_dict()) {
+    VLOG(1) << "Expected dictionary in p3a manifest";
+    return nullptr;
+  }
+
+  const auto* metrics_dict = json_root->GetDict().FindDict(kMetricsKey);
+  if (!metrics_dict) {
+    VLOG(1) << "No metrics found in p3a manifest";
+    return nullptr;
+  }
+
+  base::JSONValueConverter<RemoteMetricConfig> converter;
+
+  auto remote_metric_configs =
+      std::make_unique<base::flat_map<std::string, RemoteMetricConfig>>();
+
+  for (const auto [metric_name, config_value] : *metrics_dict) {
+    if (!config_value.is_dict()) {
+      VLOG(1) << "Metric config for " << metric_name << " is not a dictionary";
+      continue;
+    }
+
+    if (!converter.Convert(config_value,
+                           &(*remote_metric_configs)[metric_name])) {
+      VLOG(1) << "Failed to convert metric config for " << metric_name;
+      remote_metric_configs->erase(metric_name);
+      continue;
+    }
+  }
+
+  return remote_metric_configs;
+}
+
+}  // namespace
+
+RemoteConfigManager::RemoteConfigManager(Delegate* delegate)
+    : delegate_(delegate) {}
path: components/p3a/remote_config_manager.cc
user: login: iefremov
body: also delegate methods seems const, perhaps we could accept const pointer?
created_at: 2025-05-13T21:09:22Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,165 @@
+/* Copyright (c) 2025 The Brave Authors. All rights reserved.
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at https://mozilla.org/MPL/2.0/. */
+
+#include "brave/components/p3a/remote_config_manager.h"
+
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "base/containers/flat_map.h"
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/json/json_reader.h"
+#include "base/json/json_value_converter.h"
+#include "base/logging.h"
+#include "base/task/thread_pool.h"
+#include "base/values.h"
+#include "brave/components/p3a/metric_config.h"
+
+constexpr char kMetricsKey[] = "metrics";
+
+namespace p3a {
+
+namespace {
+
+// Reads and parses the p3a_manifest.json file from disk
+std::unique_ptr<base::flat_map<std::string, RemoteMetricConfig>>
+ReadAndParseJsonRules(const base::FilePath& manifest_file_path) {
+  std::string raw_contents;
+  if (!base::ReadFileToString(manifest_file_path, &raw_contents)) {
+    VLOG(1) << "Failed to read p3a manifest";
+    return nullptr;
+  }
+
+  const auto json_root = base::JSONReader::Read(raw_contents);
+  if (!json_root) {
+    VLOG(1) << "Failed to parse p3a manifest";
+    return nullptr;
+  }
+
+  if (!json_root->is_dict()) {
+    VLOG(1) << "Expected dictionary in p3a manifest";
+    return nullptr;
+  }
+
+  const auto* metrics_dict = json_root->GetDict().FindDict(kMetricsKey);
+  if (!metrics_dict) {
+    VLOG(1) << "No metrics found in p3a manifest";
+    return nullptr;
+  }
+
+  base::JSONValueConverter<RemoteMetricConfig> converter;
+
+  auto remote_metric_configs =
+      std::make_unique<base::flat_map<std::string, RemoteMetricConfig>>();
+
+  for (const auto [metric_name, config_value] : *metrics_dict) {
+    if (!config_value.is_dict()) {
+      VLOG(1) << "Metric config for " << metric_name << " is not a dictionary";
+      continue;
+    }
+
+    if (!converter.Convert(config_value,
+                           &(*remote_metric_configs)[metric_name])) {
+      VLOG(1) << "Failed to convert metric config for " << metric_name;
+      remote_metric_configs->erase(metric_name);
+      continue;
+    }
+  }
+
+  return remote_metric_configs;
+}
+
+}  // namespace
+
+RemoteConfigManager::RemoteConfigManager(Delegate* delegate)
+    : delegate_(delegate) {}
+
+RemoteConfigManager::~RemoteConfigManager() = default;
+
+void RemoteConfigManager::OnComponentReady(const base::FilePath& install_dir) {
+  LoadRemoteConfig(install_dir);
+}
+
+void RemoteConfigManager::LoadRemoteConfig(const base::FilePath& install_dir) {
+  base::FilePath manifest_file_path =
+      install_dir.AppendASCII(kP3AManifestFileName);
+
+  base::ThreadPool::PostTaskAndReplyWithResult(
+      FROM_HERE, {base::MayBlock()},
+      base::BindOnce(&ReadAndParseJsonRules, manifest_file_path),
+      base::BindOnce(&RemoteConfigManager::SetMetricConfigs,
+                     weak_factory_.GetWeakPtr()));
+}
+
+void RemoteConfigManager::SetMetricConfigs(
+    std::unique_ptr<base::flat_map<std::string, RemoteMetricConfig>> result) {
+  if (!result) {
+    return;
+  }
+
+  VLOG(1) << "Loaded " << result->size() << " metric configurations";
+
+  activation_metric_names_.clear();
+
+  for (const auto& entry : *result) {
+    if (!delegate_->GetLogTypeForHistogram(entry.first)) {
+      continue;
+    }
+
+    const auto* base_config = delegate_->GetBaseMetricConfig(entry.first);
+
+    const auto& remote_config = entry.second;
+
path: components/p3a/remote_config_manager.cc
user: login: iefremov
body: let's compress the vertical whitespace
created_at: 2025-05-13T21:09:56Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,155 @@
+/* Copyright (c) 2025 The Brave Authors. All rights reserved.
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at https://mozilla.org/MPL/2.0/. */
+
+#include "brave/components/p3a/metric_config.h"
+
+#include "base/containers/fixed_flat_map.h"
+
+namespace p3a {
+
+namespace {
+
+constexpr auto kMetricAttributeMap =
+    base::MakeFixedFlatMap<std::string_view, MetricAttribute>({
+        {"answer_index", MetricAttribute::kAnswerIndex},
+        {"version", MetricAttribute::kVersion},
+        {"yoi", MetricAttribute::kYoi},
+        {"channel", MetricAttribute::kChannel},
+        {"platform", MetricAttribute::kPlatform},
+        {"country_code", MetricAttribute::kCountryCode},
+        {"woi", MetricAttribute::kWoi},
+        {"general_platform", MetricAttribute::kGeneralPlatform},
+        {"region", MetricAttribute::kRegion},
+        {"subregion", MetricAttribute::kSubregion},
+        {"ref", MetricAttribute::kRef},
+        {"dtoi", MetricAttribute::kDateOfInstall},
+        {"dtoa", MetricAttribute::kDateOfActivation},
+    });
+
+bool GetMetricAttribute(const base::Value* value,
+                        std::optional<MetricAttribute>* field) {
+  if (!value || !value->is_string()) {
+    return false;
+  }
+
+  const auto& str = value->GetString();
+  auto it = kMetricAttributeMap.find(str);
+  if (it == kMetricAttributeMap.end()) {
+    return false;
+  }
+  *field = it->second;
+  return true;
+}
+
+bool GetMetricLogType(const base::Value* value,
+                      std::optional<MetricLogType>* field) {
+  if (!value || !value->is_string()) {
+    return false;
+  }
+
+  auto log_type = StringToMetricLogType(value->GetString());
+  if (!log_type) {
+    return false;
+  }
+  *field = log_type.value();
+  return true;
+}
+
+bool GetMetricAttributes(const base::Value* value,
+                         std::optional<MetricAttributes>* field) {
+  if (!value || !value->is_list()) {
+    return false;
+  }
+
+  const auto& list = value->GetList();
+
+  MetricAttributes attributes;
+
+  for (size_t i = 0; i < list.size() && i < attributes.size(); i++) {
+    if (!GetMetricAttribute(&list[i], &attributes[i])) {
+      return false;
+    }
+  }
+
+  *field = attributes;
+  return true;
+}
+
+bool GetAppendAttributes(const base::Value* value,
+                         std::optional<MetricAttributesToAppend>* field) {
+  if (!value || !value->is_list()) {
+    return false;
+  }
+
+  const auto& list = value->GetList();
+
+  MetricAttributesToAppend attributes;
+
+  for (size_t i = 0; i < list.size() && i < attributes.size(); i++) {
+    if (!GetMetricAttribute(&list[i], &attributes[i])) {
+      return false;
+    }
+  }
+
+  *field = attributes;
+
+  return true;
+}
+
+bool GetOptionalString(const base::Value* value,
+                       std::optional<std::string>* field) {
+  if (!value || !value->is_string()) {
+    return false;
+  }
+
+  *field = value->GetString();
+  return true;
+}
+
+bool GetOptionalBool(const base::Value* value, std::optional<bool>* field) {
+  if (!value || !value->is_bool()) {
+    return false;
+  }
+  *field = value->GetBool();
+  return true;
+}
+
+}  // namespace
+
+RemoteMetricConfig::RemoteMetricConfig() = default;
+RemoteMetricConfig::~RemoteMetricConfig() = default;
+
+RemoteMetricConfig::RemoteMetricConfig(const RemoteMetricConfig&) = default;
+RemoteMetricConfig& RemoteMetricConfig::operator=(const RemoteMetricConfig&) =
+    default;
+
+void RemoteMetricConfig::RegisterJSONConverter(
+    base::JSONValueConverter<RemoteMetricConfig>* converter) {
+  converter->RegisterCustomValueField(
+      "ephemeral", &RemoteMetricConfig::ephemeral, &GetOptionalBool);
path: components/p3a/metric_config.cc
user: login: DJAndries
body: The default converter does not support `std::optional<bool>`. The optional is needed in the struct so we can decide whether to override the setting in the base configuration. For example, if we used `bool` instead of the optional variant, and a base metric config marks a metric as ephemeral, and the matching remote config does not specify the ephemeral property, then the metric will become non-ephemeral which is undesired.
created_at: 2025-05-13T21:51:28Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,112 @@
+/* Copyright (c) 2025 The Brave Authors. All rights reserved.
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at https://mozilla.org/MPL/2.0/. */
+
+#include "brave/components/p3a/component_installer.h"
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "brave/components/brave_component_updater/browser/brave_on_demand_updater.h"
+#include "brave/components/p3a/remote_config_manager.h"
+#include "components/component_updater/component_installer.h"
+#include "components/component_updater/component_updater_service.h"
+
+using component_updater::ComponentUpdateService;
+
+namespace p3a {
+
+namespace {
+
+constexpr char kP3AComponentName[] = "P3A Configuration";
+constexpr char kP3AComponentId[] = "memmkfnkoigleebghlpgeiecaddoblcl";
+constexpr base::FilePath::CharType kComponentInstallDir[] =
+    FILE_PATH_LITERAL("P3AConfig");
+// public key hash:
+// c4cca5dae86b44167bf64842033e1b2b5c908bd6abc30e32d08b12bf50b2cc03
+const uint8_t kP3AComponentHash[32] = {
+    0xc4, 0xcc, 0xa5, 0xda, 0xe8, 0x6b, 0x44, 0x16, 0x7b, 0xf6, 0x48,
+    0x42, 0x03, 0x3e, 0x1b, 0x2b, 0x5c, 0x90, 0x8b, 0xd6, 0xab, 0xc3,
+    0x0e, 0x32, 0xd0, 0x8b, 0x12, 0xbf, 0x50, 0xb2, 0xcc, 0x03};
+
+class P3AComponentInstallerPolicy
+    : public component_updater::ComponentInstallerPolicy {
+ public:
+  explicit P3AComponentInstallerPolicy(
+      base::WeakPtr<RemoteConfigManager> remote_config_manager)
+      : remote_config_manager_(remote_config_manager) {}
+  ~P3AComponentInstallerPolicy() override = default;
+
+  P3AComponentInstallerPolicy(const P3AComponentInstallerPolicy&) = delete;
+  P3AComponentInstallerPolicy& operator=(const P3AComponentInstallerPolicy&) =
+      delete;
+
+ private:
+  // component_updater::ComponentInstallerPolicy:
+  bool SupportsGroupPolicyEnabledComponentUpdates() const override {
+    return false;
+  }
+
+  bool RequiresNetworkEncryption() const override { return false; }
+
+  update_client::CrxInstaller::Result OnCustomInstall(
+      const base::Value::Dict& manifest,
+      const base::FilePath& install_dir) override {
+    return update_client::CrxInstaller::Result(0);
+  }
+
+  void OnCustomUninstall() override {}
+
+  bool VerifyInstallation(const base::Value::Dict& manifest,
+                          const base::FilePath& install_dir) const override {
+    return base::PathExists(install_dir.Append(kP3AManifestFileName));
+  }
+
+  void ComponentReady(const base::Version& version,
+                      const base::FilePath& install_dir,
+                      base::Value::Dict manifest) override {
+    remote_config_manager_->OnComponentReady(install_dir);
+  }
+
+  base::FilePath GetRelativeInstallDir() const override {
+    return base::FilePath(kComponentInstallDir);
+  }
+
+  void GetHash(std::vector<uint8_t>* hash) const override {
+    hash->assign(std::begin(kP3AComponentHash), std::end(kP3AComponentHash));
+  }
+
+  std::string GetName() const override { return kP3AComponentName; }
+
+  update_client::InstallerAttributes GetInstallerAttributes() const override {
+    return update_client::InstallerAttributes();
+  }
+
+  bool IsBraveComponent() const override { return true; }
+
+  base::WeakPtr<RemoteConfigManager> remote_config_manager_;
path: components/p3a/component_installer.cc
user: login: DJAndries
body: added pointer check
created_at: 2025-05-13T22:33:56Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,112 @@
+/* Copyright (c) 2025 The Brave Authors. All rights reserved.
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at https://mozilla.org/MPL/2.0/. */
+
+#include "brave/components/p3a/component_installer.h"
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "brave/components/brave_component_updater/browser/brave_on_demand_updater.h"
+#include "brave/components/p3a/remote_config_manager.h"
+#include "components/component_updater/component_installer.h"
+#include "components/component_updater/component_updater_service.h"
+
+using component_updater::ComponentUpdateService;
+
+namespace p3a {
+
+namespace {
+
+constexpr char kP3AComponentName[] = "P3A Configuration";
+constexpr char kP3AComponentId[] = "memmkfnkoigleebghlpgeiecaddoblcl";
+constexpr base::FilePath::CharType kComponentInstallDir[] =
+    FILE_PATH_LITERAL("P3AConfig");
+// public key hash:
+// c4cca5dae86b44167bf64842033e1b2b5c908bd6abc30e32d08b12bf50b2cc03
+const uint8_t kP3AComponentHash[32] = {
+    0xc4, 0xcc, 0xa5, 0xda, 0xe8, 0x6b, 0x44, 0x16, 0x7b, 0xf6, 0x48,
+    0x42, 0x03, 0x3e, 0x1b, 0x2b, 0x5c, 0x90, 0x8b, 0xd6, 0xab, 0xc3,
+    0x0e, 0x32, 0xd0, 0x8b, 0x12, 0xbf, 0x50, 0xb2, 0xcc, 0x03};
+
+class P3AComponentInstallerPolicy
+    : public component_updater::ComponentInstallerPolicy {
+ public:
+  explicit P3AComponentInstallerPolicy(
+      base::WeakPtr<RemoteConfigManager> remote_config_manager)
+      : remote_config_manager_(remote_config_manager) {}
+  ~P3AComponentInstallerPolicy() override = default;
+
+  P3AComponentInstallerPolicy(const P3AComponentInstallerPolicy&) = delete;
+  P3AComponentInstallerPolicy& operator=(const P3AComponentInstallerPolicy&) =
+      delete;
+
+ private:
+  // component_updater::ComponentInstallerPolicy:
+  bool SupportsGroupPolicyEnabledComponentUpdates() const override {
+    return false;
+  }
+
+  bool RequiresNetworkEncryption() const override { return false; }
+
+  update_client::CrxInstaller::Result OnCustomInstall(
+      const base::Value::Dict& manifest,
+      const base::FilePath& install_dir) override {
+    return update_client::CrxInstaller::Result(0);
+  }
+
+  void OnCustomUninstall() override {}
+
+  bool VerifyInstallation(const base::Value::Dict& manifest,
+                          const base::FilePath& install_dir) const override {
+    return base::PathExists(install_dir.Append(kP3AManifestFileName));
+  }
+
+  void ComponentReady(const base::Version& version,
+                      const base::FilePath& install_dir,
+                      base::Value::Dict manifest) override {
+    remote_config_manager_->OnComponentReady(install_dir);
+  }
+
+  base::FilePath GetRelativeInstallDir() const override {
+    return base::FilePath(kComponentInstallDir);
+  }
+
+  void GetHash(std::vector<uint8_t>* hash) const override {
+    hash->assign(std::begin(kP3AComponentHash), std::end(kP3AComponentHash));
+  }
+
+  std::string GetName() const override { return kP3AComponentName; }
+
+  update_client::InstallerAttributes GetInstallerAttributes() const override {
+    return update_client::InstallerAttributes();
+  }
+
+  bool IsBraveComponent() const override { return true; }
+
+  base::WeakPtr<RemoteConfigManager> remote_config_manager_;
+};
+
+}  // namespace
+
+void RegisterP3AComponent(
+    ComponentUpdateService* cus,
+    base::WeakPtr<RemoteConfigManager> remote_config_manager) {
path: components/p3a/component_installer.cc
user: login: DJAndries
body: added pointer check
created_at: 2025-05-13T22:34:07Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,114 @@
+/* Copyright (c) 2025 The Brave Authors. All rights reserved.
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at https://mozilla.org/MPL/2.0/. */
+
+#include "brave/components/p3a/component_installer.h"
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "brave/components/brave_component_updater/browser/brave_on_demand_updater.h"
+#include "brave/components/p3a/remote_config_manager.h"
+#include "components/component_updater/component_installer.h"
+#include "components/component_updater/component_updater_service.h"
+
+using component_updater::ComponentUpdateService;
+
+namespace p3a {
+
+namespace {
+
+constexpr char kP3AComponentName[] = "P3A Configuration";
+constexpr char kP3AComponentId[] = "memmkfnkoigleebghlpgeiecaddoblcl";
+constexpr base::FilePath::CharType kComponentInstallDir[] =
+    FILE_PATH_LITERAL("P3AConfig");
+// public key hash:
+// c4cca5dae86b44167bf64842033e1b2b5c908bd6abc30e32d08b12bf50b2cc03
+const uint8_t kP3AComponentHash[32] = {
+    0xc4, 0xcc, 0xa5, 0xda, 0xe8, 0x6b, 0x44, 0x16, 0x7b, 0xf6, 0x48,
+    0x42, 0x03, 0x3e, 0x1b, 0x2b, 0x5c, 0x90, 0x8b, 0xd6, 0xab, 0xc3,
+    0x0e, 0x32, 0xd0, 0x8b, 0x12, 0xbf, 0x50, 0xb2, 0xcc, 0x03};
+
+class P3AComponentInstallerPolicy
+    : public component_updater::ComponentInstallerPolicy {
+ public:
+  explicit P3AComponentInstallerPolicy(
+      base::WeakPtr<RemoteConfigManager> remote_config_manager)
+      : remote_config_manager_(remote_config_manager) {}
+  ~P3AComponentInstallerPolicy() override = default;
+
+  P3AComponentInstallerPolicy(const P3AComponentInstallerPolicy&) = delete;
+  P3AComponentInstallerPolicy& operator=(const P3AComponentInstallerPolicy&) =
+      delete;
+
+ private:
+  // component_updater::ComponentInstallerPolicy:
+  bool SupportsGroupPolicyEnabledComponentUpdates() const override {
+    return false;
+  }
+
+  bool RequiresNetworkEncryption() const override { return false; }
+
+  update_client::CrxInstaller::Result OnCustomInstall(
+      const base::Value::Dict& manifest,
+      const base::FilePath& install_dir) override {
+    return update_client::CrxInstaller::Result(0);
+  }
+
+  void OnCustomUninstall() override {}
+
+  bool VerifyInstallation(const base::Value::Dict& manifest,
+                          const base::FilePath& install_dir) const override {
+    return base::PathExists(install_dir.Append(kP3AManifestFileName));
+  }
+
+  void ComponentReady(const base::Version& version,
+                      const base::FilePath& install_dir,
+                      base::Value::Dict manifest) override {
+    if (remote_config_manager_) {
+      remote_config_manager_->LoadRemoteConfig(install_dir, base::DoNothing());
path: components/p3a/component_installer.cc
user: login: iefremov
body: actually i would load the file from disk in this class, to simplify the manager (to avoid using files, task runner, etc in it), as this is kinda established pattern - policy classes  loading the file. So just pass raw contents to the manager and it would do json parsing without loading the file

Also thinking of the remote manager handle, i'd basically just pass a lambda to this class, i.e. bind a weak pointer to `LoadRemoteConfig`, pass the lambda to `P3AComponentInstallerPolicy` and call it when the file is loaded.

The first point is more important, the second is matter of taste, just looks simpler
created_at: 2025-05-14T10:09:22Z
author_association: CONTRIBUTOR
diff_hunk: @@ -58,8 +58,40 @@ class P3AMessageManagerTest : public testing::Test,
                 &url_loader_factory_)) {}
 
   std::optional<MetricLogType> GetDynamicMetricLogType(
-      const std::string& histogram_name) const override {
-    return std::optional<MetricLogType>();
+      std::string_view histogram_name) const override {
+    return std::nullopt;
+  }
+
+  const MetricConfig* GetMetricConfig(
+      std::string_view histogram_name) const override {
+    const std::optional<MetricConfig>* metric_config = nullptr;
+
+    auto it = p3a::kCollectedTypicalHistograms.find(histogram_name);
path: components/p3a/message_manager_unittest.cc
user: login: iefremov
body: is it copypaste? can't we make a helper function and invoke it from two call sites?
created_at: 2025-05-14T10:11:17Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,114 @@
+/* Copyright (c) 2025 The Brave Authors. All rights reserved.
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at https://mozilla.org/MPL/2.0/. */
+
+#include "brave/components/p3a/component_installer.h"
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "brave/components/brave_component_updater/browser/brave_on_demand_updater.h"
+#include "brave/components/p3a/remote_config_manager.h"
+#include "components/component_updater/component_installer.h"
+#include "components/component_updater/component_updater_service.h"
+
+using component_updater::ComponentUpdateService;
+
+namespace p3a {
+
+namespace {
+
+constexpr char kP3AComponentName[] = "P3A Configuration";
+constexpr char kP3AComponentId[] = "memmkfnkoigleebghlpgeiecaddoblcl";
+constexpr base::FilePath::CharType kComponentInstallDir[] =
+    FILE_PATH_LITERAL("P3AConfig");
+// public key hash:
+// c4cca5dae86b44167bf64842033e1b2b5c908bd6abc30e32d08b12bf50b2cc03
+const uint8_t kP3AComponentHash[32] = {
+    0xc4, 0xcc, 0xa5, 0xda, 0xe8, 0x6b, 0x44, 0x16, 0x7b, 0xf6, 0x48,
+    0x42, 0x03, 0x3e, 0x1b, 0x2b, 0x5c, 0x90, 0x8b, 0xd6, 0xab, 0xc3,
+    0x0e, 0x32, 0xd0, 0x8b, 0x12, 0xbf, 0x50, 0xb2, 0xcc, 0x03};
+
+class P3AComponentInstallerPolicy
+    : public component_updater::ComponentInstallerPolicy {
+ public:
+  explicit P3AComponentInstallerPolicy(
+      base::WeakPtr<RemoteConfigManager> remote_config_manager)
+      : remote_config_manager_(remote_config_manager) {}
+  ~P3AComponentInstallerPolicy() override = default;
+
+  P3AComponentInstallerPolicy(const P3AComponentInstallerPolicy&) = delete;
+  P3AComponentInstallerPolicy& operator=(const P3AComponentInstallerPolicy&) =
+      delete;
+
+ private:
+  // component_updater::ComponentInstallerPolicy:
+  bool SupportsGroupPolicyEnabledComponentUpdates() const override {
+    return false;
+  }
+
+  bool RequiresNetworkEncryption() const override { return false; }
+
+  update_client::CrxInstaller::Result OnCustomInstall(
+      const base::Value::Dict& manifest,
+      const base::FilePath& install_dir) override {
+    return update_client::CrxInstaller::Result(0);
+  }
+
+  void OnCustomUninstall() override {}
+
+  bool VerifyInstallation(const base::Value::Dict& manifest,
+                          const base::FilePath& install_dir) const override {
+    return base::PathExists(install_dir.Append(kP3AManifestFileName));
+  }
+
+  void ComponentReady(const base::Version& version,
+                      const base::FilePath& install_dir,
+                      base::Value::Dict manifest) override {
+    if (remote_config_manager_) {
+      remote_config_manager_->LoadRemoteConfig(install_dir, base::DoNothing());
path: components/p3a/component_installer.cc
user: login: DJAndries
body: It seems that there are quite a few cases where we load the component files outside of the installer policy.

i.e.

- https://github.com/brave/brave-core/blob/c28fda2a248533d290ecfcaf6a85aba27307e232/components/brave_user_agent/browser/brave_user_agent_component_installer.cc#L95-L100
- https://github.com/brave/brave-core/blob/c28fda2a248533d290ecfcaf6a85aba27307e232/components/psst/browser/core/psst_component_installer.cc#L116-L120
- https://github.com/brave/brave-core/blob/c28fda2a248533d290ecfcaf6a85aba27307e232/components/brave_wallet/browser/wallet_data_files_installer.cc#L105-L111
- https://github.com/brave/brave-core/blob/c28fda2a248533d290ecfcaf6a85aba27307e232/components/playlist/browser/media_detector_component_installer.cc#L108-L113

In addition, I would probably need to dispatch tasks anyway in order to parse JSON without blocking the UI thread. Keeping the status quo also makes the unit test situation easier, as I can test both reading and parsing in the `RemoteConfigManager` test.
created_at: 2025-05-14T21:57:18Z
author_association: COLLABORATOR
diff_hunk: @@ -58,8 +58,40 @@ class P3AMessageManagerTest : public testing::Test,
                 &url_loader_factory_)) {}
 
   std::optional<MetricLogType> GetDynamicMetricLogType(
-      const std::string& histogram_name) const override {
-    return std::optional<MetricLogType>();
+      std::string_view histogram_name) const override {
+    return std::nullopt;
+  }
+
+  const MetricConfig* GetMetricConfig(
+      std::string_view histogram_name) const override {
+    const std::optional<MetricConfig>* metric_config = nullptr;
+
+    auto it = p3a::kCollectedTypicalHistograms.find(histogram_name);
path: components/p3a/message_manager_unittest.cc
user: login: DJAndries
body: good point
created_at: 2025-05-14T22:49:48Z
author_association: COLLABORATOR

PR comments:
user: login: github-actions[bot]
created_at: 2025-04-16T17:18:23Z
body: Chromium major version is behind target branch (135.0.7049.84 vs 136.0.7103.25). Please rebase.
author_association: CONTRIBUTOR
user: login: iefremov
created_at: 2025-05-13T21:28:12Z
body: pls also fix the presubmit warnings
author_association: CONTRIBUTOR
user: login: github-actions[bot]
created_at: 2025-05-14T00:03:27Z
body: Chromium major version is behind target branch (136.0.7103.93 vs 137.0.7151.16). Please rebase.
author_association: CONTRIBUTOR
user: login: github-actions[bot]
created_at: 2025-05-30T20:16:07Z
body: [[puLL-Merge](https://github.com/brave/pull-merge)] - [brave/brave-core@28362](https://github.com/brave/brave-core/pull/28362)



### Description
This PR introduces remote configuration support for P3A (Privacy-Preserving Product Analytics) metrics in the Brave browser. The changes add a component updater system that can dynamically download and apply configuration changes to P3A metrics without requiring browser updates. This includes a new `RemoteConfigManager` that loads configuration from a JSON manifest file delivered via the component updater system, allowing real-time modification of metric collection behavior, cadence, and attributes.

<details>
<summary><i>Changes</i></summary>

### Changes

**chromium_src/chrome/browser/component_updater/DEPS & registration.cc**
- Added P3A component dependency and registration
- Integrated P3A component installer with the browser's component update system

**components/p3a/BUILD.gn**
- Added new source files for component installer, remote config manager, and metric config utilities
- Added dependencies on component updater and brave component updater

**components/p3a/component_installer.{cc,h}**
- New component installer for P3A configuration updates
- Handles downloading and installing remote P3A configuration files
- Uses component ID `memmkfnkoigleebghlpgeiecaddoblcl` with specific public key hash

**components/p3a/remote_config_manager.{cc,h}**
- Manages loading and parsing of remote P3A configurations from JSON manifest
- Merges remote configs with base metric configurations
- Provides interface for querying metric configurations

**components/p3a/metric_config.{cc,h}**
- Extended `MetricConfig` struct with new `cadence` field for dynamic log type override
- Added `RemoteMetricConfig` struct for JSON deserialization
- Added JSON converter for parsing remote configuration data

**components/p3a/metric_config_utils.{cc,h}**
- Utility functions to retrieve base metric configurations and log types
- Extracted from `MessageManager` for better code organization

**components/p3a/p3a_service.cc**
- Integrated `RemoteConfigManager` with P3A service
- Modified initialization flow to wait for remote config loading
- Updated metric configuration lookup to prioritize remote configs over base configs
- Added support for dynamic cadence overrides from remote configuration

**components/p3a/message_manager.{cc,h}**
- Refactored metric configuration lookup to use delegate pattern
- Added `RemoveObsoleteLogs()` method for cleaning up metrics that are no longer valid
- Updated constellation message generation to use pointer instead of optional for metric config

**components/p3a/metric_log_store.{cc,h}**
- Added `RemoveObsoleteLogs()` method to clean up logs for metrics with changed log types
- Improved handling of obsolete metrics during store operations

**test/data/p3a/p3a_manifest.json**
- Test configuration file demonstrating remote metric configuration format



```mermaid
sequenceDiagram
    participant CUS as ComponentUpdateService
    participant CI as ComponentInstaller
    participant RCM as RemoteConfigManager
    participant P3AS as P3AService
    participant MM as MessageManager

    Note over CUS,MM: Initialization Flow
    CUS->>CI: Register P3A component
    CI->>CUS: Component registered
    
    Note over CUS,MM: Remote Config Update
    CUS->>CI: Component updated
    CI->>RCM: LoadRemoteConfig(install_dir)
    RCM->>RCM: Parse p3a_manifest.json
    RCM->>P3AS: OnRemoteConfigLoaded()
    P3AS->>MM: RemoveObsoleteLogs()
    MM->>MM: Clean up obsolete metrics
    
    Note over P3AS,MM: Metric Processing
    P3AS->>P3AS: HandleHistogramChange()
    P3AS->>RCM: GetMetricConfig(histogram_name)
    alt Remote config exists
        RCM-->>P3AS: Remote MetricConfig
    else No remote config
        P3AS->>P3AS: GetBaseMetricConfig()
        P3AS-->>P3AS: Base MetricConfig
    end
    P3AS->>MM: UpdateMetricValue()
    MM->>MM: Process metric with config
```
</details>

### Possible Issues

1. **Initialization Race Condition**: The P3A service initialization is delayed until remote config is loaded, which could potentially delay metric collection startup. On iOS, this check is bypassed with a buildflag guard.

2. **Memory Management**: The remote config manager stores metric configurations in memory, which could grow if many metrics are configured remotely. However, this is likely bounded by the reasonable number of P3A metrics.

3. **Component Update Dependency**: The system now depends on the component updater working correctly. If component updates fail, the service falls back to base configurations, but this dependency adds complexity.

4. **JSON Parsing**: The JSON parsing is done on a background thread but failures are handled gracefully by falling back to base configurations.

### Security Hotspots

1. **Component Verification**: The component installer uses a hardcoded public key hash (`kP3AComponentHash`) to verify component authenticity. This is a security-critical mechanism that prevents unauthorized configuration injection.

2. **File System Access**: The remote config manager reads configuration files from the component installation directory. The file reading is done with `base::ReadFileToString()` which should be safe, but the path comes from the component updater system.

3. **JSON Deserialization**: Remote configuration is parsed from JSON using Chromium's JSON parser. The parsing includes validation of field types and values, reducing the risk of malformed data causing issues.

4. **Configuration Validation**: The system validates that remote configurations only apply to known metrics (those with valid log types), preventing arbitrary metric creation through remote configuration.

<!-- Generated by claude-sonnet-4-20250514 -->
author_association: CONTRIBUTOR
user: login: brave-builds
created_at: 2025-06-02T00:06:03Z
body: Released in [v1.81.35](https://github.com/brave/brave-browser/releases/tag/v1.81.35)
author_association: COLLABORATOR