| // 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. |
| |
| #include "chrome/browser/ash/settings/owner_pending_setting_controller.h" |
| |
| #include <string> |
| |
| #include "base/functional/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/device_settings_service.h" |
| #include "chrome/browser/profiles/profile.h" |
| #include "chromeos/ash/components/settings/cros_settings.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, base::Value value) { |
| DCHECK(profile); |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| |
| if (GetOwnershipStatus() == |
| DeviceSettingsService::OwnershipStatus::kOwnershipTaken) { |
| // 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), std::move(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(); |
| } |
| } |
| |
| std::optional<base::Value> OwnerPendingSettingController::GetValue() const { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| std::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::OwnershipStatus::kOwnershipTaken); |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| VLOG(1) << "OnOwnershipTaken"; |
| |
| if (std::optional<base::Value> pending_value = GetPendingValue()) { |
| // 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, *std::move(pending_value)); |
| } |
| } |
| |
| OwnerPendingSettingController::~OwnerPendingSettingController() { |
| owner_settings_service_observation_.Reset(); |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| } |
| |
| void OwnerPendingSettingController::OnSignedPolicyStored(bool success) { |
| if (!success) |
| return; |
| std::optional<base::Value> pending_value = GetPendingValue(); |
| std::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::OnServiceShutdown() { |
| owner_settings_service_observation_.Reset(); |
| } |
| |
| 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. |
| 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(), std::move(value))); |
| } 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_` must be set to `true` and |
| // `pending_pref_name_` must be set with `value` before setting `pref_name_` |
| // with `OwnerSettingService` to guarantee that future calls to `GetValue()` |
| // returns the pending value instead of the signed value until |
| // `OnSignedPolicyStored(true)` is called. |
| // Since calls to `GetValue()` can happen inside `service->Set()`, the order |
| // of the following lines is important. |
| is_value_being_set_with_service_ = true; |
| local_state_->Set(pending_pref_name_, value); |
| |
| service->Set(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_); |
| std::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); |
| } |
| |
| std::optional<base::Value> OwnerPendingSettingController::GetPendingValue() |
| const { |
| if (local_state_->HasPrefPath(pending_pref_name_)) { |
| return local_state_->GetValue(pending_pref_name_).Clone(); |
| } |
| return std::nullopt; |
| } |
| |
| void OwnerPendingSettingController::ClearPendingValue() { |
| VLOG(1) << "ClearPendingValue"; |
| local_state_->ClearPref(pending_pref_name_); |
| } |
| |
| std::optional<base::Value> OwnerPendingSettingController::GetSignedStoredValue() |
| const { |
| const base::Value* value = CrosSettings::Get()->GetPref(pref_name_); |
| if (value) { |
| return value->Clone(); |
| } |
| return std::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::OwnershipStatus::kOwnershipNone || |
| GetOwnershipStatus() == |
| DeviceSettingsService::OwnershipStatus::kOwnershipUnknown) { |
| 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 |