chromium/chrome/browser/ash/settings/owner_pending_setting_controller.h

// Copyright 2021 The Chromium Authors
// 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/raw_ptr.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/device_settings_service.h"
#include "chromeos/ash/components/settings/cros_settings.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 the device pref.
// 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 std::nullopt if there is no pending write and no signed value.
  std::optional<base::Value> GetValue() const;

  // Add an observer |callback| for changes to the setting.
  [[nodiscard]] base::CallbackListSubscription AddObserver(
      const base::RepeatingClosure& callback);

  // 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_);

  raw_ptr<PrefService> local_state_;
  std::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.
  std::optional<base::Value> GetPendingValue() const;

  // Return the value signed and stored in CrosSettings, if one exists.
  std::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_