File: owner_pending_setting_controller.cc

package info (click to toggle)
chromium 139.0.7258.127-1
  • links: PTS, VCS
  • area: main
  • in suites:
  • size: 6,122,068 kB
  • sloc: cpp: 35,100,771; ansic: 7,163,530; javascript: 4,103,002; python: 1,436,920; asm: 946,517; xml: 746,709; pascal: 187,653; perl: 88,691; sh: 88,436; objc: 79,953; sql: 51,488; cs: 44,583; fortran: 24,137; makefile: 22,147; tcl: 15,277; php: 13,980; yacc: 8,984; ruby: 7,485; awk: 3,720; lisp: 3,096; lex: 1,327; ada: 727; jsp: 228; sed: 36
file content (230 lines) | stat: -rw-r--r-- 8,928 bytes parent folder | download | duplicates (5)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
// 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