[CloudReady][OOBE] Add controller to write setting before ownership
kRevenEnableDeviceHWDataUsage cros setting can be set during user
onboarding. HWDataUsageController class allows writing device settings
before ownership of the device is taken.
Bug: b:190727230
Change-Id: I71260bcaa4896cc82bb2d5284e99739574e0ce3c
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3301468
Reviewed-by: Roman Sorokin <rsorokin@chromium.org>
Reviewed-by: David Roger <droger@chromium.org>
Commit-Queue: Danila Kuzmin <dkuzmin@google.com>
Cr-Commit-Position: refs/heads/main@{#949507}
diff --git a/chrome/browser/ash/login/screens/hardware_data_collection_screen.cc b/chrome/browser/ash/login/screens/hardware_data_collection_screen.cc
index 09eb9d2..bf900d1 100644
--- a/chrome/browser/ash/login/screens/hardware_data_collection_screen.cc
+++ b/chrome/browser/ash/login/screens/hardware_data_collection_screen.cc
@@ -8,6 +8,7 @@
#include "ash/constants/ash_switches.h"
#include "chrome/browser/ash/policy/core/browser_policy_connector_ash.h"
+#include "chrome/browser/ash/settings/hardware_data_usage_controller.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/browser_process_platform_part.h"
#include "chrome/browser/profiles/profile_manager.h"
@@ -94,7 +95,8 @@
void HWDataCollectionScreen::OnUserAction(const std::string& action_id) {
if (action_id == kUserActionAcceptButtonClicked) {
- // TODO(dkuzmin): set a device setting here, once it will be available
+ HWDataUsageController::Get()->Set(ProfileManager::GetActiveUserProfile(),
+ base::Value(hw_data_usage_enabled_));
exit_callback_.Run(hw_data_usage_enabled_
? Result::ACCEPTED_WITH_HW_DATA_USAGE
: Result::ACCEPTED_WITHOUT_HW_DATA_USAGE);
diff --git a/chrome/browser/ash/settings/device_settings_provider.cc b/chrome/browser/ash/settings/device_settings_provider.cc
index b7b5167..d17694e5 100644
--- a/chrome/browser/ash/settings/device_settings_provider.cc
+++ b/chrome/browser/ash/settings/device_settings_provider.cc
@@ -29,6 +29,7 @@
#include "chrome/browser/ash/policy/off_hours/off_hours_proto_parser.h"
#include "chrome/browser/ash/settings/cros_settings.h"
#include "chrome/browser/ash/settings/device_settings_cache.h"
+#include "chrome/browser/ash/settings/hardware_data_usage_controller.h"
#include "chrome/browser/ash/settings/stats_reporting_controller.h"
#include "chrome/browser/ash/tpm_firmware_update.h"
#include "chromeos/dbus/dbus_thread_manager.h"
@@ -1409,18 +1410,21 @@
// TODO(https://crbug.com/433840): Some of the above code can be
// simplified or removed, once the DoSet function is removed - then there
- // will be no pending writes. This is because the only value that needs to
- // be written as a pending write is kStatsReportingPref, and this is now
- // handled by the StatsReportingController - see below. Once DoSet is
- // removed and there are no pending writes that are being maintained by
- // DeviceSettingsProvider, this code for updating the signed settings for
- // the new owner should probably be moved outside of
- // DeviceSettingsProvider.
+ // will be no pending writes. This is because the only values that need to
+ // be written as a pending write is kStatsReportingPref and
+ // kEnableDeviceHWDataUsage, and those are now handled by the Controllers
+ // - see below. Once DoSet is removed and there are no pending writes that
+ // are being maintained by DeviceSettingsProvider, this code for updating
+ // the signed settings for the new owner should probably be moved outside
+ // of DeviceSettingsProvider.
StatsReportingController::Get()->OnOwnershipTaken(
device_settings_service_->GetOwnerSettingsService());
+ HWDataUsageController::Get()->OnOwnershipTaken(
+ device_settings_service_->GetOwnerSettingsService());
} else if (chromeos::InstallAttributes::Get()->IsEnterpriseManaged()) {
StatsReportingController::Get()->ClearPendingValue();
+ HWDataUsageController::Get()->ClearPendingValue();
}
}
diff --git a/chrome/browser/ash/settings/hardware_data_usage_controller.cc b/chrome/browser/ash/settings/hardware_data_usage_controller.cc
new file mode 100644
index 0000000..74b09a3
--- /dev/null
+++ b/chrome/browser/ash/settings/hardware_data_usage_controller.cc
@@ -0,0 +1,69 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ash/settings/hardware_data_usage_controller.h"
+
+#include "ash/components/settings/cros_settings_names.h"
+#include "base/bind.h"
+#include "chrome/browser/ash/settings/cros_settings.h"
+#include "components/prefs/pref_registry_simple.h"
+#include "components/prefs/pref_service.h"
+
+namespace {
+
+constexpr char kPendingPref[] = "pending.cros.reven.enable_hw_data_usage";
+
+} // namespace
+
+namespace ash {
+
+static HWDataUsageController* g_hw_data_usage_controller = nullptr;
+
+// static
+void HWDataUsageController::Initialize(PrefService* local_state) {
+ CHECK(!g_hw_data_usage_controller);
+ g_hw_data_usage_controller = new HWDataUsageController(local_state);
+}
+
+// static
+bool HWDataUsageController::IsInitialized() {
+ return g_hw_data_usage_controller;
+}
+
+// static
+void HWDataUsageController::Shutdown() {
+ DCHECK(g_hw_data_usage_controller);
+ delete g_hw_data_usage_controller;
+ g_hw_data_usage_controller = nullptr;
+}
+
+// static
+HWDataUsageController* HWDataUsageController::Get() {
+ CHECK(g_hw_data_usage_controller);
+ return g_hw_data_usage_controller;
+}
+
+// static
+void HWDataUsageController::RegisterLocalStatePrefs(
+ PrefRegistrySimple* registry) {
+ registry->RegisterBooleanPref(kPendingPref, false,
+ PrefRegistry::NO_REGISTRATION_FLAGS);
+}
+
+HWDataUsageController::HWDataUsageController(PrefService* local_state)
+ : OwnerPendingSettingController(kRevenEnableDeviceHWDataUsage,
+ kPendingPref,
+ local_state) {
+ setting_subscription_ = CrosSettings::Get()->AddSettingsObserver(
+ kRevenEnableDeviceHWDataUsage,
+ base::BindRepeating(&HWDataUsageController::NotifyObservers,
+ this->as_weak_ptr()));
+}
+
+HWDataUsageController::~HWDataUsageController() {
+ owner_settings_service_observation_.Reset();
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+}
+
+} // namespace ash
diff --git a/chrome/browser/ash/settings/hardware_data_usage_controller.h b/chrome/browser/ash/settings/hardware_data_usage_controller.h
new file mode 100644
index 0000000..ea48de46
--- /dev/null
+++ b/chrome/browser/ash/settings/hardware_data_usage_controller.h
@@ -0,0 +1,43 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_ASH_SETTINGS_HARDWARE_DATA_USAGE_CONTROLLER_H_
+#define CHROME_BROWSER_ASH_SETTINGS_HARDWARE_DATA_USAGE_CONTROLLER_H_
+
+#include "chrome/browser/ash/settings/owner_pending_setting_controller.h"
+
+class PrefRegistrySimple;
+class PrefService;
+
+namespace ash {
+
+// Class to control setting of cros.reven.enable_hw_data_usage device preference
+// before ownership is taken.
+class HWDataUsageController : public OwnerPendingSettingController {
+ public:
+ // Manage singleton instance.
+ static void Initialize(PrefService* local_state);
+ static bool IsInitialized();
+ static void Shutdown();
+ static HWDataUsageController* Get();
+
+ HWDataUsageController(const HWDataUsageController&) = delete;
+ HWDataUsageController& operator=(const HWDataUsageController&) = delete;
+
+ static void RegisterLocalStatePrefs(PrefRegistrySimple* registry);
+
+ private:
+ explicit HWDataUsageController(PrefService* local_state);
+ ~HWDataUsageController() override;
+};
+
+} // namespace ash
+
+// TODO(https://crbug.com/1164001): remove when Chrome OS code migration is
+// done.
+namespace chromeos {
+using ::ash::HWDataUsageController;
+} // namespace chromeos
+
+#endif // CHROME_BROWSER_ASH_SETTINGS_HARDWARE_DATA_USAGE_CONTROLLER_H_
diff --git a/chrome/browser/ash/settings/owner_pending_setting_controller.cc b/chrome/browser/ash/settings/owner_pending_setting_controller.cc
new file mode 100644
index 0000000..36ed811
--- /dev/null
+++ b/chrome/browser/ash/settings/owner_pending_setting_controller.cc
@@ -0,0 +1,215 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ash/settings/owner_pending_setting_controller.h"
+
+#include <string>
+
+#include "base/bind.h"
+#include "base/logging.h"
+#include "chrome/browser/ash/ownership/owner_settings_service_ash.h"
+#include "chrome/browser/ash/ownership/owner_settings_service_ash_factory.h"
+#include "chrome/browser/ash/settings/cros_settings.h"
+#include "chrome/browser/ash/settings/device_settings_service.h"
+#include "chrome/browser/profiles/profile.h"
+#include "components/ownership/owner_settings_service.h"
+#include "components/prefs/pref_registry_simple.h"
+#include "components/prefs/pref_service.h"
+
+namespace ash {
+
+OwnerPendingSettingController::OwnerPendingSettingController(
+ const std::string& pref_name,
+ const std::string& pending_pref_name,
+ PrefService* local_state)
+ : local_state_(local_state),
+ pref_name_(pref_name),
+ pending_pref_name_(pending_pref_name) {
+ value_notified_to_observers_ = GetValue();
+}
+
+void OwnerPendingSettingController::Set(Profile* profile,
+ const base::Value& value) {
+ DCHECK(profile);
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+ if (GetOwnershipStatus() == DeviceSettingsService::OWNERSHIP_TAKEN) {
+ // The device has an owner. If the current profile is that owner, we will
+ // write the value on their behalf, otherwise no action is taken.
+ VLOG(1) << "Already has owner";
+ SetWithServiceAsync(GetOwnerSettingsService(profile), value);
+ } else {
+ // The device has no owner, or we do not know yet whether the device has an
+ // owner. We write a pending value that will be persisted when ownership is
+ // taken (if that has not already happened).
+ // We store the new value in the local state, so that even if Chrome is
+ // restarted before ownership is taken, we will still persist it eventually.
+ // See OnOwnershipTaken.
+ VLOG(1) << "Pending owner; setting pending pref name: "
+ << pending_pref_name_;
+ local_state_->Set(pending_pref_name_, value);
+ NotifyObservers();
+ }
+}
+
+absl::optional<base::Value> OwnerPendingSettingController::GetValue() const {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ absl::optional<base::Value> value = GetPendingValue();
+ if (ShouldReadFromPendingValue() && value.has_value()) {
+ // Return the pending value if it exists.
+ return value;
+ }
+ // Otherwise, always return the value from the signed store.
+ return GetSignedStoredValue();
+}
+
+base::CallbackListSubscription OwnerPendingSettingController::AddObserver(
+ const base::RepeatingClosure& callback) {
+ DCHECK(!callback.is_null());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ return callback_list_.Add(callback);
+}
+
+void OwnerPendingSettingController::OnOwnershipTaken(
+ ownership::OwnerSettingsService* service) {
+ DCHECK_EQ(GetOwnershipStatus(), DeviceSettingsService::OWNERSHIP_TAKEN);
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ VLOG(1) << "OnOwnershipTaken";
+
+ absl::optional<base::Value> pending_value = GetPendingValue();
+ if (pending_value.has_value()) {
+ // At the time ownership is taken, there is a value waiting to be written.
+ // Use the OwnerSettingsService of the new owner to write the setting.
+ SetWithServiceAsync(service, pending_value.value());
+ }
+}
+
+OwnerPendingSettingController::~OwnerPendingSettingController() {
+ owner_settings_service_observation_.Reset();
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+}
+
+void OwnerPendingSettingController::OnSignedPolicyStored(bool success) {
+ if (!success)
+ return;
+ absl::optional<base::Value> pending_value = GetPendingValue();
+ absl::optional<base::Value> signed_value = GetSignedStoredValue();
+ if (pending_value.has_value() && signed_value.has_value() &&
+ pending_value == signed_value) {
+ is_value_being_set_with_service_ = false;
+ owner_settings_service_observation_.Reset();
+ ClearPendingValue();
+ NotifyObservers();
+ if (on_device_settings_stored_callback_)
+ std::move(on_device_settings_stored_callback_).Run();
+ }
+}
+
+void OwnerPendingSettingController::SetOnDeviceSettingsStoredCallBack(
+ base::OnceClosure callback) {
+ CHECK(!on_device_settings_stored_callback_);
+ on_device_settings_stored_callback_ = std::move(callback);
+}
+
+void OwnerPendingSettingController::SetWithServiceAsync(
+ ownership::OwnerSettingsService* service, // Can be null for non-owners.
+ const base::Value& value) {
+ bool not_yet_ready = service && !service->IsReady();
+ if (not_yet_ready) {
+ VLOG(1) << "Service not yet ready. Adding listener.";
+ // Service is not yet ready. Listen for changes in its readiness so we can
+ // write the value once it is ready. Uses weak pointers, so if everything
+ // is shutdown and deleted in the meantime, this callback isn't run.
+ service->IsOwnerAsync(base::BindOnce(
+ &OwnerPendingSettingController::SetWithServiceCallback,
+ this->as_weak_ptr(), service->as_weak_ptr(), value.Clone()));
+ } else {
+ // Service is either null, or ready - use it right now.
+ SetWithService(service, value);
+ }
+}
+
+void OwnerPendingSettingController::SetWithServiceCallback(
+ const base::WeakPtr<ownership::OwnerSettingsService>& service,
+ const base::Value value,
+ bool is_owner) {
+ if (service) // Make sure service wasn't deleted in the meantime.
+ SetWithService(service.get(), value);
+}
+
+void OwnerPendingSettingController::SetWithService(
+ ownership::OwnerSettingsService* service, // Can be null for non-owners.
+ const base::Value& value) {
+ if (service && service->IsOwner()) {
+ if (!owner_settings_service_observation_.IsObserving())
+ owner_settings_service_observation_.Observe(service);
+ is_value_being_set_with_service_ = true;
+ service->Set(pref_name_, value);
+ local_state_->Set(pending_pref_name_, value);
+ } else {
+ // Do nothing since we are not the owner.
+ LOG(WARNING) << "Changing settings from non-owner, setting=" << pref_name_;
+ }
+}
+
+void OwnerPendingSettingController::NotifyObservers() {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ absl::optional<base::Value> current_value = GetValue();
+ if (current_value != value_notified_to_observers_) {
+ VLOG(1) << "Notifying observers";
+ value_notified_to_observers_ = std::move(current_value);
+ callback_list_.Notify();
+ } else {
+ VLOG(1) << "Not notifying (already notified)";
+ }
+}
+
+DeviceSettingsService::OwnershipStatus
+OwnerPendingSettingController::GetOwnershipStatus() const {
+ return DeviceSettingsService::Get()->GetOwnershipStatus();
+}
+
+ownership::OwnerSettingsService*
+OwnerPendingSettingController::GetOwnerSettingsService(Profile* profile) {
+ return OwnerSettingsServiceAshFactory::GetForBrowserContext(profile);
+}
+
+absl::optional<base::Value> OwnerPendingSettingController::GetPendingValue()
+ const {
+ if (local_state_->HasPrefPath(pending_pref_name_)) {
+ return local_state_->Get(pending_pref_name_)->Clone();
+ }
+ return absl::nullopt;
+}
+
+void OwnerPendingSettingController::ClearPendingValue() {
+ VLOG(1) << "ClearPendingValue";
+ local_state_->ClearPref(pending_pref_name_);
+}
+
+absl::optional<base::Value>
+OwnerPendingSettingController::GetSignedStoredValue() const {
+ const base::Value* value = CrosSettings::Get()->GetPref(pref_name_);
+ if (value) {
+ return value->Clone();
+ }
+ return absl::nullopt;
+}
+
+bool OwnerPendingSettingController::ShouldReadFromPendingValue() const {
+ // Read from pending value before ownership is taken or ownership is
+ // unknown. There's a brief moment when ownership is unknown for every
+ // Chrome starts. In that case, we will read from pending value if it exists
+ // (which means ownership is not taken), and read from service when pending
+ // value pending is cleared (which means ownership is taken).
+ if (GetOwnershipStatus() == DeviceSettingsService::OWNERSHIP_NONE ||
+ GetOwnershipStatus() == DeviceSettingsService::OWNERSHIP_UNKNOWN) {
+ return true;
+ }
+ // Read from pending value if ownership is taken but pending value has not
+ // been set successfully with service.
+ return is_value_being_set_with_service_;
+}
+
+} // namespace ash
diff --git a/chrome/browser/ash/settings/owner_pending_setting_controller.h b/chrome/browser/ash/settings/owner_pending_setting_controller.h
new file mode 100644
index 0000000..7e06c67
--- /dev/null
+++ b/chrome/browser/ash/settings/owner_pending_setting_controller.h
@@ -0,0 +1,165 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_ASH_SETTINGS_OWNER_PENDING_SETTING_CONTROLLER_H_
+#define CHROME_BROWSER_ASH_SETTINGS_OWNER_PENDING_SETTING_CONTROLLER_H_
+
+#include "base/callback_list.h"
+#include "base/memory/weak_ptr.h"
+#include "base/scoped_observation.h"
+#include "base/sequence_checker.h"
+#include "base/values.h"
+#include "chrome/browser/ash/settings/cros_settings.h"
+#include "chrome/browser/ash/settings/device_settings_service.h"
+#include "components/ownership/owner_settings_service.h"
+
+class PrefService;
+class Profile;
+
+namespace ownership {
+class OwnerSettingsService;
+}
+
+namespace ash {
+
+// An extra layer on top of CrosSettings / OwnerSettingsService that allows for
+// writing a setting before ownership is taken.
+//
+// Ordinarily, the OwnerSettingsService interface is used for writing settings,
+// and the CrosSettings interface is used for reading them - but as the OSS
+// cannot be used until the device has an owner, this class can be used instead,
+// since writing the new value with SetEnabled works even before ownership is
+// taken.
+//
+// If OSS is ready then the new value is written straight away, and if not, then
+// a pending write is queued that is completed as soon as the OSS is ready.
+// This write will complete even if Chrome is restarted in the meantime.
+// The caller need not care whether the write was immediate or pending, as long
+// as they also use this class to read the value of kStatsReportingPref.
+// IsEnabled will return the pending value until ownership is taken and the
+// pending value is written - from then on it will return the signed, stored
+// value from CrosSettings.
+class OwnerPendingSettingController
+ : public ownership::OwnerSettingsService::Observer {
+ public:
+ OwnerPendingSettingController() = delete;
+ OwnerPendingSettingController(const OwnerPendingSettingController&) = delete;
+ OwnerPendingSettingController& operator=(
+ const OwnerPendingSettingController&) = delete;
+
+ // Store the new value. This will happen straight away if |profile| is the
+ // owner, and it will cause a pending write to be buffered and written later
+ // if the device has no owner yet. It will write a warning and skip if the
+ // device already has an owner, and |profile| is not that owner.
+ void Set(Profile* profile, const base::Value& value);
+
+ // Returns the latest value - regardless of whether this has been successfully
+ // signed and persisted, or if it is still stored as a pending write. Can
+ // return absl::nullopt if there is no pending write and no signed value.
+ absl::optional<base::Value> GetValue() const;
+
+ // Add an observer |callback| for changes to the setting.
+ base::CallbackListSubscription AddObserver(
+ const base::RepeatingClosure& callback) WARN_UNUSED_RESULT;
+
+ // Called once ownership is taken, |service| is the service of the user taking
+ // ownership.
+ void OnOwnershipTaken(ownership::OwnerSettingsService* service);
+
+ // Sets the callback which is called once when the pending |value| is
+ // propagated to the device settings. Support only one callback at a time.
+ // CHECKs if the second callback is being set.
+ // It's different from the |AddObserver| API. Observers are called
+ // immediately after |Set| is called with the different |value| setting.
+ void SetOnDeviceSettingsStoredCallBack(base::OnceClosure callback);
+
+ // ownership::OwnerSettingsService::Observer implementation:
+ void OnSignedPolicyStored(bool success) override;
+
+ // Clears any value waiting to be written (from storage in local state).
+ void ClearPendingValue();
+
+ protected:
+ OwnerPendingSettingController(const std::string& pref_name,
+ const std::string& pending_pref_name,
+ PrefService* local_state);
+ ~OwnerPendingSettingController() override;
+
+ // Delegates immediately to SetWithService if |service| is ready, otherwise
+ // runs SetWithService asynchronously once |service| is ready.
+ void SetWithServiceAsync(ownership::OwnerSettingsService* service,
+ const base::Value& value);
+
+ // Callback used by SetWithServiceAsync.
+ void SetWithServiceCallback(
+ const base::WeakPtr<ownership::OwnerSettingsService>& service,
+ const base::Value value,
+ bool is_owner);
+
+ // Uses |service| to write the latest value, as long as |service| belongs
+ // to the owner - otherwise just prints a warning.
+ void SetWithService(ownership::OwnerSettingsService* service,
+ const base::Value& value);
+
+ // Notifies observers if the value has changed.
+ void NotifyObservers();
+
+ base::WeakPtr<OwnerPendingSettingController> as_weak_ptr() {
+ return weak_factory_.GetWeakPtr();
+ }
+
+ SEQUENCE_CHECKER(sequence_checker_);
+
+ PrefService* local_state_;
+ absl::optional<base::Value> value_notified_to_observers_;
+ base::RepeatingClosureList callback_list_;
+ base::CallbackListSubscription setting_subscription_;
+
+ base::ScopedObservation<ownership::OwnerSettingsService,
+ ownership::OwnerSettingsService::Observer>
+ owner_settings_service_observation_{this};
+
+ // Indicates if the setting value is in the process of being set with the
+ // service. There is a small period of time needed between start saving the
+ // value and before the value is stored correctly in the service. We should
+ // not use the setting value from the service if it is still in the process
+ // of being saved.
+ bool is_value_being_set_with_service_ = false;
+
+ private:
+ friend class StatsReportingControllerTest;
+
+ // Gets the current ownership status - owned, unowned, or unknown.
+ DeviceSettingsService::OwnershipStatus GetOwnershipStatus() const;
+
+ // Get the owner-settings service for a particular profile. A variety of
+ // different results can be returned, depending on the profile.
+ // a) A ready-to-use service that we know belongs to the owner.
+ // b) A ready-to-use service that we know does NOT belong to the owner.
+ // c) A service that is NOT ready-to-use, which MIGHT belong to the owner.
+ // d) nullptr (for instance, if |profile| is a guest).
+ ownership::OwnerSettingsService* GetOwnerSettingsService(Profile* profile);
+
+ // Return the value waiting to be written (stored in local_state), if one
+ // exists.
+ absl::optional<base::Value> GetPendingValue() const;
+
+ // Return the value signed and stored in CrosSettings, if one exists.
+ absl::optional<base::Value> GetSignedStoredValue() const;
+
+ // Returns whether pending value should be used when determining the value
+ // of `GetValue`.
+ bool ShouldReadFromPendingValue() const;
+
+ base::OnceClosure on_device_settings_stored_callback_;
+
+ const std::string pref_name_;
+ const std::string pending_pref_name_;
+
+ base::WeakPtrFactory<OwnerPendingSettingController> weak_factory_{this};
+};
+
+} // namespace ash
+
+#endif // CHROME_BROWSER_ASH_SETTINGS_OWNER_PENDING_SETTING_CONTROLLER_H_
diff --git a/chrome/browser/ash/settings/stats_reporting_controller.cc b/chrome/browser/ash/settings/stats_reporting_controller.cc
index 11ea28d05..652327565 100644
--- a/chrome/browser/ash/settings/stats_reporting_controller.cc
+++ b/chrome/browser/ash/settings/stats_reporting_controller.cc
@@ -4,19 +4,12 @@
#include "chrome/browser/ash/settings/stats_reporting_controller.h"
-#include <string>
-
#include "ash/components/settings/cros_settings_names.h"
#include "base/bind.h"
#include "base/logging.h"
-#include "chrome/browser/ash/ownership/owner_settings_service_ash.h"
-#include "chrome/browser/ash/ownership/owner_settings_service_ash_factory.h"
#include "chrome/browser/ash/settings/cros_settings.h"
-#include "chrome/browser/ash/settings/device_settings_service.h"
-#include "chrome/browser/profiles/profile.h"
#include "components/metrics/structured/neutrino_logging.h"
#include "components/metrics/structured/neutrino_logging_util.h"
-#include "components/ownership/owner_settings_service.h"
#include "components/prefs/pref_registry_simple.h"
#include "components/prefs/pref_service.h"
@@ -62,92 +55,31 @@
}
void StatsReportingController::SetEnabled(Profile* profile, bool enabled) {
- DCHECK(profile);
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- VLOG(1) << "SetEnabled(" << std::boolalpha << enabled << ")";
-
- if (GetOwnershipStatus() == DeviceSettingsService::OWNERSHIP_TAKEN) {
- // The device has an owner. If the current profile is that owner, we will
- // write the value on their behalf, otherwise no action is taken.
- VLOG(1) << "Already has owner";
- SetWithServiceAsync(GetOwnerSettingsService(profile), enabled);
- } else {
- // The device has no owner, or we do not know yet whether the device has an
- // owner. We write a pending value that will be persisted when ownership is
- // taken (if that has not already happened).
- // We store the new value in the local state, so that even if Chrome is
- // restarted before ownership is taken, we will still persist it eventually.
- // See OnOwnershipTaken.
- VLOG(1) << "Pending owner; setting kPendingPref";
- local_state_->SetBoolean(kPendingPref, enabled);
- NotifyObservers();
- }
+ Set(profile, base::Value(enabled));
}
-bool StatsReportingController::IsEnabled() {
+bool StatsReportingController::IsEnabled() const {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- bool value = false;
metrics::structured::NeutrinoDevicesLogWithLocalState(
local_state_, metrics::structured::NeutrinoDevicesLocation::kIsEnabled);
- if (ShouldReadFromPendingValue() && GetPendingValue(&value)) {
- // Return the pending value if it exists.
- return value;
- }
- // Otherwise, always return the value from the signed store.
- GetSignedStoredValue(&value);
- return value;
-}
-
-base::CallbackListSubscription StatsReportingController::AddObserver(
- const base::RepeatingClosure& callback) {
- DCHECK(!callback.is_null());
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- return callback_list_.Add(callback);
-}
-
-void StatsReportingController::OnOwnershipTaken(
- ownership::OwnerSettingsService* service) {
- DCHECK_EQ(GetOwnershipStatus(), DeviceSettingsService::OWNERSHIP_TAKEN);
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- VLOG(1) << "OnOwnershipTaken";
-
- bool pending_value;
- if (GetPendingValue(&pending_value)) {
- // At the time ownership is taken, there is a value waiting to be written.
- // Use the OwnerSettingsService of the new owner to write the setting.
- SetWithServiceAsync(service, pending_value);
- }
-}
-
-void StatsReportingController::OnSignedPolicyStored(bool success) {
- if (!success)
- return;
- bool pending_value;
- bool signed_value;
- if (GetPendingValue(&pending_value) && GetSignedStoredValue(&signed_value) &&
- pending_value == signed_value) {
- is_value_being_set_with_service_ = false;
- owner_settings_service_observation_.Reset();
- ClearPendingValue();
- NotifyObservers();
- if (on_device_settings_stored_callback_)
- std::move(on_device_settings_stored_callback_).Run();
- }
-}
-
-void StatsReportingController::SetOnDeviceSettingsStoredCallBack(
- base::OnceClosure callback) {
- CHECK(!on_device_settings_stored_callback_);
- on_device_settings_stored_callback_ = std::move(callback);
+ absl::optional<base::Value> value = GetValue();
+ return value.has_value() && value->is_bool() && value->GetBool();
}
StatsReportingController::StatsReportingController(PrefService* local_state)
- : local_state_(local_state) {
+ : OwnerPendingSettingController(kStatsReportingPref,
+ kPendingPref,
+ local_state) {
setting_subscription_ = CrosSettings::Get()->AddSettingsObserver(
kStatsReportingPref,
base::BindRepeating(&StatsReportingController::NotifyObservers,
this->as_weak_ptr()));
- value_notified_to_observers_ = IsEnabled();
+ // AddObserver to log NeutrinoDevicesLogWithLocalState every time when
+ // observers are notified.
+ neutrino_logging_subscription_ = AddObserver(base::BindRepeating(
+ &metrics::structured::NeutrinoDevicesLogWithLocalState, local_state_,
+ metrics::structured::NeutrinoDevicesLocation::kNotifyObservers));
}
StatsReportingController::~StatsReportingController() {
@@ -155,107 +87,4 @@
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
}
-void StatsReportingController::SetWithServiceAsync(
- ownership::OwnerSettingsService* service, // Can be null for non-owners.
- bool enabled) {
- VLOG(1) << "SetWithServiceAsync(" << std::boolalpha << enabled << ")";
- bool not_yet_ready = service && !service->IsReady();
- if (not_yet_ready) {
- VLOG(1) << "Service not yet ready. Adding listener.";
- // Service is not yet ready. Listen for changes in its readiness so we can
- // write the value once it is ready. Uses weak pointers, so if everything
- // is shutdown and deleted in the meantime, this callback isn't run.
- service->IsOwnerAsync(
- base::BindOnce(&StatsReportingController::SetWithServiceCallback,
- this->as_weak_ptr(), service->as_weak_ptr(), enabled));
- } else {
- // Service is either null, or ready - use it right now.
- SetWithService(service, enabled);
- }
-}
-
-void StatsReportingController::SetWithServiceCallback(
- const base::WeakPtr<ownership::OwnerSettingsService>& service,
- bool enabled,
- bool is_owner) {
- VLOG(1) << "SetWithServiceCallback(" << std::boolalpha << enabled << ")";
- if (service) // Make sure service wasn't deleted in the meantime.
- SetWithService(service.get(), enabled);
-}
-
-void StatsReportingController::SetWithService(
- ownership::OwnerSettingsService* service, // Can be null for non-owners.
- bool enabled) {
- VLOG(1) << "SetWithService(" << std::boolalpha << enabled << ")";
- if (service && service->IsOwner()) {
- if (!owner_settings_service_observation_.IsObserving())
- owner_settings_service_observation_.Observe(service);
- is_value_being_set_with_service_ = true;
- service->SetBoolean(kStatsReportingPref, enabled);
- local_state_->SetBoolean(kPendingPref, enabled);
- } else {
- // Do nothing since we are not the owner.
- LOG(WARNING) << "Changing settings from non-owner, setting="
- << kStatsReportingPref;
- }
-}
-
-void StatsReportingController::NotifyObservers() {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- bool current_value = IsEnabled();
- VLOG(1) << "Maybe notifying observers of " << std::boolalpha << current_value;
- if (current_value != value_notified_to_observers_) {
- VLOG(1) << "Notifying observers";
- value_notified_to_observers_ = current_value;
- metrics::structured::NeutrinoDevicesLogWithLocalState(
- local_state_,
- metrics::structured::NeutrinoDevicesLocation::kNotifyObservers);
- callback_list_.Notify();
- } else {
- VLOG(1) << "Not notifying (already notified)";
- }
-}
-
-DeviceSettingsService::OwnershipStatus
-StatsReportingController::GetOwnershipStatus() const {
- return DeviceSettingsService::Get()->GetOwnershipStatus();
-}
-
-ownership::OwnerSettingsService*
-StatsReportingController::GetOwnerSettingsService(Profile* profile) {
- return OwnerSettingsServiceAshFactory::GetForBrowserContext(profile);
-}
-
-bool StatsReportingController::GetPendingValue(bool* result) {
- if (local_state_->HasPrefPath(kPendingPref)) {
- *result = local_state_->GetBoolean(kPendingPref);
- return true;
- }
- return false;
-}
-
-void StatsReportingController::ClearPendingValue() {
- VLOG(1) << "ClearPendingValue";
- local_state_->ClearPref(kPendingPref);
-}
-
-bool StatsReportingController::GetSignedStoredValue(bool* result) {
- return CrosSettings::Get()->GetBoolean(kStatsReportingPref, result);
-}
-
-bool StatsReportingController::ShouldReadFromPendingValue() const {
- // Read from pending value before ownership is taken or ownership is
- // unknown. There's a brief moment when ownership is unknown for every
- // Chrome starts. In that case, we will read from pending value if it exists
- // (which means ownership is not taken), and read from service when pending
- // value pending is cleared (which means ownership is taken).
- if (GetOwnershipStatus() == DeviceSettingsService::OWNERSHIP_NONE ||
- GetOwnershipStatus() == DeviceSettingsService::OWNERSHIP_UNKNOWN) {
- return true;
- }
- // Read from pending value if ownership is taken but pending value has not
- // been set successfully with service.
- return is_value_being_set_with_service_;
-}
-
} // namespace ash
diff --git a/chrome/browser/ash/settings/stats_reporting_controller.h b/chrome/browser/ash/settings/stats_reporting_controller.h
index 9a29d47..e2f8f22 100644
--- a/chrome/browser/ash/settings/stats_reporting_controller.h
+++ b/chrome/browser/ash/settings/stats_reporting_controller.h
@@ -5,44 +5,18 @@
#ifndef CHROME_BROWSER_ASH_SETTINGS_STATS_REPORTING_CONTROLLER_H_
#define CHROME_BROWSER_ASH_SETTINGS_STATS_REPORTING_CONTROLLER_H_
-#include "base/callback_list.h"
+#include "chrome/browser/ash/settings/owner_pending_setting_controller.h"
+
#include "base/memory/weak_ptr.h"
-#include "base/scoped_observation.h"
-#include "base/sequence_checker.h"
-#include "chrome/browser/ash/settings/cros_settings.h"
-#include "chrome/browser/ash/settings/device_settings_service.h"
-#include "components/ownership/owner_settings_service.h"
class PrefRegistrySimple;
class PrefService;
-class Profile;
-
-namespace ownership {
-class OwnerSettingsService;
-}
namespace ash {
-// An extra layer on top of CrosSettings / OwnerSettingsService that allows for
-// writing a setting before ownership is taken, for one setting only:
-// kStatsReportingPref, which has the key: "cros.metrics.reportingEnabled".
-//
-// Ordinarily, the OwnerSettingsService interface is used for writing settings,
-// and the CrosSettings interface is used for reading them - but as the OSS
-// cannot be used until the device has an owner, this class can be used instead,
-// since writing the new value with SetEnabled works even before ownership is
-// taken.
-//
-// If OSS is ready then the new value is written straight away, and if not, then
-// a pending write is queued that is completed as soon as the OSS is ready.
-// This write will complete even if Chrome is restarted in the meantime.
-// The caller need not care whether the write was immediate or pending, as long
-// as they also use this class to read the value of kStatsReportingPref.
-// IsEnabled will return the pending value until ownership is taken and the
-// pending value is written - from then on it will return the signed, stored
-// value from CrosSettings.
-class StatsReportingController
- : public ownership::OwnerSettingsService::Observer {
+// Class to control setting of cros.metrics.reportingEnabled device preference
+// before ownership is taken.
+class StatsReportingController : public OwnerPendingSettingController {
public:
// Manage singleton instance.
static void Initialize(PrefService* local_state);
@@ -55,109 +29,18 @@
static void RegisterLocalStatePrefs(PrefRegistrySimple* registry);
- // Store the new value of |enabled|. This will happen straight away if
- // |profile| is the owner, and it will cause a pending write to be buffered
- // and written later if the device has no owner yet. It will fail if the
- // device already has an owner, and |profile| is not that owner.
void SetEnabled(Profile* profile, bool enabled);
-
- // Returns the latest value of enabled - regardless of whether this has been
- // successfully signed and persisted, or if it is still stored as a pending
- // write.
- bool IsEnabled();
-
- // Add an observer |callback| for changes to the setting.
- base::CallbackListSubscription AddObserver(
- const base::RepeatingClosure& callback) WARN_UNUSED_RESULT;
-
- // Called once ownership is taken, |service| is the service of the user taking
- // ownership.
- void OnOwnershipTaken(ownership::OwnerSettingsService* service);
-
- // ownership::OwnerSettingsService::Observer implementation:
- void OnSignedPolicyStored(bool success) override;
-
- // Sets the callback which is called once when the |enabled| value is
- // propagated to the device settings. Support only one callback at a time.
- // CHECKs if the second callback is being set.
- // It's different from the |AddObserver| API. Observers are called
- // immediately after |SetEnabled| is called with the different |enabled|
- // setting.
- void SetOnDeviceSettingsStoredCallBack(base::OnceClosure callback);
-
- // Clears any value waiting to be written (from storage in local state).
- void ClearPendingValue();
+ bool IsEnabled() const;
private:
- friend class StatsReportingControllerTest;
-
explicit StatsReportingController(PrefService* local_state);
~StatsReportingController() override;
- // Delegates immediately to SetWithService if |service| is ready, otherwise
- // runs SetWithService asynchronously once |service| is ready.
- void SetWithServiceAsync(ownership::OwnerSettingsService* service,
- bool enabled);
-
- // Callback used by SetWithServiceAsync.
- void SetWithServiceCallback(
- const base::WeakPtr<ownership::OwnerSettingsService>& service,
- bool enabled,
- bool is_owner);
-
- // Uses |service| to write the latest value, as long as |service| belongs
- // to the owner - otherwise just prints a warning.
- void SetWithService(ownership::OwnerSettingsService* service, bool enabled);
-
- // Notifies observers if the value has changed.
- void NotifyObservers();
-
- // Gets the current ownership status - owned, unowned, or unknown.
- DeviceSettingsService::OwnershipStatus GetOwnershipStatus() const;
-
- // Get the owner-settings service for a particular profile. A variety of
- // different results can be returned, depending on the profile.
- // a) A ready-to-use service that we know belongs to the owner.
- // b) A ready-to-use service that we know does NOT belong to the owner.
- // c) A service that is NOT ready-to-use, which MIGHT belong to the owner.
- // d) nullptr (for instance, if |profile| is a guest).
- ownership::OwnerSettingsService* GetOwnerSettingsService(Profile* profile);
-
- // Sets |*value| to the value waiting to be written (stored in local_state),
- // if one exists. Returns false if there is no such value.
- bool GetPendingValue(bool* value);
-
- // Sets |*value| to the value signed and stored in CrosSettings, if one
- // exists. Returns false if there is no such value.
- bool GetSignedStoredValue(bool* value);
-
- // Returns whether pending value should be used when determining the value
- // of `IsEnabled`.
- bool ShouldReadFromPendingValue() const;
-
base::WeakPtr<StatsReportingController> as_weak_ptr() {
return weak_factory_.GetWeakPtr();
}
- SEQUENCE_CHECKER(sequence_checker_);
-
- PrefService* local_state_;
- bool value_notified_to_observers_;
- base::RepeatingClosureList callback_list_;
- base::CallbackListSubscription setting_subscription_;
-
- // Indicates if the setting value is in the process of being set with the
- // service. There is a small period of time needed between start saving the
- // value and before the value is stored correctly in the service. We should
- // not use the setting value from the service if it is still in the process
- // of being saved.
- bool is_value_being_set_with_service_ = false;
-
- base::ScopedObservation<ownership::OwnerSettingsService,
- ownership::OwnerSettingsService::Observer>
- owner_settings_service_observation_{this};
-
- base::OnceClosure on_device_settings_stored_callback_;
+ base::CallbackListSubscription neutrino_logging_subscription_;
base::WeakPtrFactory<StatsReportingController> weak_factory_{this};
};
diff --git a/chrome/browser/ash/settings/stats_reporting_controller_unittest.cc b/chrome/browser/ash/settings/stats_reporting_controller_unittest.cc
index c9283c1..0e17a28 100644
--- a/chrome/browser/ash/settings/stats_reporting_controller_unittest.cc
+++ b/chrome/browser/ash/settings/stats_reporting_controller_unittest.cc
@@ -67,20 +67,25 @@
}
void ExpectThatPendingValueIs(bool expected) {
- bool pending = false;
- EXPECT_TRUE(StatsReportingController::Get()->GetPendingValue(&pending));
- EXPECT_EQ(expected, pending);
+ absl::optional<base::Value> pending =
+ StatsReportingController::Get()->GetPendingValue();
+ EXPECT_TRUE(pending.has_value());
+ EXPECT_TRUE(pending->is_bool());
+ EXPECT_EQ(expected, pending->GetBool());
}
void ExpectThatPendingValueIsNotSet() {
- bool pending = false;
- EXPECT_FALSE(StatsReportingController::Get()->GetPendingValue(&pending));
+ absl::optional<base::Value> pending =
+ StatsReportingController::Get()->GetPendingValue();
+ EXPECT_FALSE(pending.has_value());
}
void ExpectThatSignedStoredValueIs(bool expected) {
- bool stored = false;
- EXPECT_TRUE(StatsReportingController::Get()->GetSignedStoredValue(&stored));
- EXPECT_EQ(expected, stored);
+ absl::optional<base::Value> stored =
+ StatsReportingController::Get()->GetSignedStoredValue();
+ EXPECT_TRUE(stored.has_value());
+ EXPECT_TRUE(stored->is_bool());
+ EXPECT_EQ(expected, stored->GetBool());
}
void OnNotifiedOfChange() {
diff --git a/chrome/browser/chrome_browser_main.cc b/chrome/browser/chrome_browser_main.cc
index 9a2def9..98556e45 100644
--- a/chrome/browser/chrome_browser_main.cc
+++ b/chrome/browser/chrome_browser_main.cc
@@ -215,6 +215,7 @@
#include "ash/components/settings/cros_settings_names.h"
#include "ash/constants/ash_switches.h"
#include "chrome/browser/ash/settings/cros_settings.h"
+#include "chrome/browser/ash/settings/hardware_data_usage_controller.h"
#include "chrome/browser/ash/settings/stats_reporting_controller.h"
#endif // BUILDFLAG(IS_CHROMEOS_ASH)
@@ -904,6 +905,7 @@
#if BUILDFLAG(IS_CHROMEOS_ASH)
ash::CrosSettings::Initialize(local_state);
+ ash::HWDataUsageController::Initialize(local_state);
ash::StatsReportingController::Initialize(local_state);
arc::StabilityMetricsManager::Initialize(local_state);
#endif // BUILDFLAG(IS_CHROMEOS_ASH)
@@ -1886,6 +1888,7 @@
device_event_log::Shutdown();
#if BUILDFLAG(IS_CHROMEOS_ASH)
+ ash::HWDataUsageController::Shutdown();
arc::StabilityMetricsManager::Shutdown();
ash::StatsReportingController::Shutdown();
ash::CrosSettings::Shutdown();
diff --git a/chrome/browser/chromeos/BUILD.gn b/chrome/browser/chromeos/BUILD.gn
index 469bbfd..560b0bc 100644
--- a/chrome/browser/chromeos/BUILD.gn
+++ b/chrome/browser/chromeos/BUILD.gn
@@ -2966,6 +2966,10 @@
"../ash/settings/device_settings_provider.h",
"../ash/settings/device_settings_service.cc",
"../ash/settings/device_settings_service.h",
+ "../ash/settings/hardware_data_usage_controller.cc",
+ "../ash/settings/hardware_data_usage_controller.h",
+ "../ash/settings/owner_pending_setting_controller.cc",
+ "../ash/settings/owner_pending_setting_controller.h",
"../ash/settings/session_manager_operation.cc",
"../ash/settings/session_manager_operation.h",
"../ash/settings/shutdown_policy_forwarder.cc",
diff --git a/chrome/browser/extensions/api/settings_private/prefs_util.cc b/chrome/browser/extensions/api/settings_private/prefs_util.cc
index fdf5198..8ffb21e 100644
--- a/chrome/browser/extensions/api/settings_private/prefs_util.cc
+++ b/chrome/browser/extensions/api/settings_private/prefs_util.cc
@@ -660,6 +660,8 @@
settings_api::PrefType::PREF_TYPE_BOOLEAN;
(*s_allowlist)[::ash::prefs::kLocalStateDevicePeripheralDataAccessEnabled] =
settings_api::PrefType::PREF_TYPE_BOOLEAN;
+ (*s_allowlist)[ash::kRevenEnableDeviceHWDataUsage] =
+ settings_api::PrefType::PREF_TYPE_BOOLEAN;
// Bluetooth & Internet settings.
(*s_allowlist)[ash::kAllowBluetooth] =
diff --git a/chrome/browser/prefs/browser_prefs.cc b/chrome/browser/prefs/browser_prefs.cc
index 8834b6ac..079d3be96 100644
--- a/chrome/browser/prefs/browser_prefs.cc
+++ b/chrome/browser/prefs/browser_prefs.cc
@@ -186,6 +186,7 @@
#include "chrome/browser/ash/net/system_proxy_manager.h"
#include "chrome/browser/ash/platform_keys/key_permissions/key_permissions_manager_impl.h"
#include "chrome/browser/ash/policy/networking/euicc_status_uploader.h"
+#include "chrome/browser/ash/settings/hardware_data_usage_controller.h"
#include "chrome/browser/ash/settings/stats_reporting_controller.h"
#include "chrome/browser/chromeos/extensions/extensions_permissions_tracker.h"
#include "chrome/browser/component_updater/metadata_table_chromeos.h"
@@ -1006,6 +1007,7 @@
ash::device_activity::DeviceActivityController::RegisterPrefs(registry);
chromeos::EnableDebuggingScreenHandler::RegisterPrefs(registry);
chromeos::FastTransitionObserver::RegisterPrefs(registry);
+ ash::HWDataUsageController::RegisterLocalStatePrefs(registry);
ash::KerberosCredentialsManager::RegisterLocalStatePrefs(registry);
ash::KioskAppManager::RegisterPrefs(registry);
ash::KioskCryptohomeRemover::RegisterPrefs(registry);