1 // Copyright (c) 2018 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "chrome/browser/chromeos/settings/stats_reporting_controller.h"
6
7 #include <string>
8
9 #include "base/bind.h"
10 #include "base/logging.h"
11 #include "chrome/browser/chromeos/ownership/owner_settings_service_chromeos.h"
12 #include "chrome/browser/chromeos/ownership/owner_settings_service_chromeos_factory.h"
13 #include "chrome/browser/chromeos/settings/cros_settings.h"
14 #include "chrome/browser/chromeos/settings/device_settings_service.h"
15 #include "chrome/browser/profiles/profile.h"
16 #include "chromeos/settings/cros_settings_names.h"
17 #include "components/ownership/owner_settings_service.h"
18 #include "components/prefs/pref_registry_simple.h"
19 #include "components/prefs/pref_service.h"
20
21 namespace {
22
23 constexpr char kPendingPref[] = "pending.cros.metrics.reportingEnabled";
24
25 } // namespace
26
27 namespace chromeos {
28
29 static StatsReportingController* g_stats_reporting_controller = nullptr;
30
31 // static
Initialize(PrefService * local_state)32 void StatsReportingController::Initialize(PrefService* local_state) {
33 CHECK(!g_stats_reporting_controller);
34 g_stats_reporting_controller = new StatsReportingController(local_state);
35 }
36
37 // static
IsInitialized()38 bool StatsReportingController::IsInitialized() {
39 return g_stats_reporting_controller;
40 }
41
42 // static
Shutdown()43 void StatsReportingController::Shutdown() {
44 DCHECK(g_stats_reporting_controller);
45 delete g_stats_reporting_controller;
46 g_stats_reporting_controller = nullptr;
47 }
48
49 // static
Get()50 StatsReportingController* StatsReportingController::Get() {
51 CHECK(g_stats_reporting_controller);
52 return g_stats_reporting_controller;
53 }
54
55 // static
RegisterLocalStatePrefs(PrefRegistrySimple * registry)56 void StatsReportingController::RegisterLocalStatePrefs(
57 PrefRegistrySimple* registry) {
58 registry->RegisterBooleanPref(kPendingPref, false,
59 PrefRegistry::NO_REGISTRATION_FLAGS);
60 }
61
SetEnabled(Profile * profile,bool enabled)62 void StatsReportingController::SetEnabled(Profile* profile, bool enabled) {
63 DCHECK(profile);
64 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
65 VLOG(1) << "SetEnabled(" << std::boolalpha << enabled << ")";
66
67 if (GetOwnershipStatus() == DeviceSettingsService::OWNERSHIP_TAKEN) {
68 // The device has an owner. If the current profile is that owner, we will
69 // write the value on their behalf, otherwise no action is taken.
70 VLOG(1) << "Already has owner";
71 SetWithServiceAsync(GetOwnerSettingsService(profile), enabled);
72 } else {
73 // The device has no owner, or we do not know yet whether the device has an
74 // owner. We write a pending value that will be persisted when ownership is
75 // taken (if that has not already happened).
76 // We store the new value in the local state, so that even if Chrome is
77 // restarted before ownership is taken, we will still persist it eventually.
78 // See OnOwnershipTaken.
79 VLOG(1) << "Pending owner; setting kPendingPref";
80 local_state_->SetBoolean(kPendingPref, enabled);
81 NotifyObservers();
82 }
83 }
84
IsEnabled()85 bool StatsReportingController::IsEnabled() {
86 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
87 bool value = false;
88 if (GetOwnershipStatus() == DeviceSettingsService::OWNERSHIP_NONE &&
89 GetPendingValue(&value)) {
90 // Return the pending value if it exists and we are sure there is no owner:
91 return value;
92 }
93 // Otherwise, always return the value from the signed store.
94 GetSignedStoredValue(&value);
95 return value;
96 }
97
98 std::unique_ptr<CrosSettings::ObserverSubscription>
AddObserver(const base::RepeatingClosure & callback)99 StatsReportingController::AddObserver(const base::RepeatingClosure& callback) {
100 DCHECK(!callback.is_null());
101 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
102 return callback_list_.Add(callback);
103 }
104
OnOwnershipTaken(ownership::OwnerSettingsService * service)105 void StatsReportingController::OnOwnershipTaken(
106 ownership::OwnerSettingsService* service) {
107 DCHECK_EQ(GetOwnershipStatus(), DeviceSettingsService::OWNERSHIP_TAKEN);
108 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
109 VLOG(1) << "OnOwnershipTaken";
110
111 bool pending_value;
112 if (GetPendingValue(&pending_value)) {
113 // At the time ownership is taken, there is a value waiting to be written.
114 // Use the OwnerSettingsService of the new owner to write the setting.
115 SetWithServiceAsync(service, pending_value);
116 }
117 }
118
StatsReportingController(PrefService * local_state)119 StatsReportingController::StatsReportingController(PrefService* local_state)
120 : local_state_(local_state) {
121 setting_subscription_ = CrosSettings::Get()->AddSettingsObserver(
122 kStatsReportingPref,
123 base::BindRepeating(&StatsReportingController::NotifyObservers,
124 this->as_weak_ptr()));
125 value_notified_to_observers_ = IsEnabled();
126 }
127
~StatsReportingController()128 StatsReportingController::~StatsReportingController() {
129 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
130 }
131
SetWithServiceAsync(ownership::OwnerSettingsService * service,bool enabled)132 void StatsReportingController::SetWithServiceAsync(
133 ownership::OwnerSettingsService* service, // Can be null for non-owners.
134 bool enabled) {
135 VLOG(1) << "SetWithServiceAsync(" << std::boolalpha << enabled << ")";
136 bool not_yet_ready = service && !service->IsReady();
137 if (not_yet_ready) {
138 VLOG(1) << "Service not yet ready. Adding listener.";
139 // Service is not yet ready. Listen for changes in its readiness so we can
140 // write the value once it is ready. Uses weak pointers, so if everything
141 // is shutdown and deleted in the meantime, this callback isn't run.
142 service->IsOwnerAsync(
143 base::BindOnce(&StatsReportingController::SetWithServiceCallback,
144 this->as_weak_ptr(), service->as_weak_ptr(), enabled));
145 } else {
146 // Service is either null, or ready - use it right now.
147 SetWithService(service, enabled);
148 }
149 }
150
SetWithServiceCallback(const base::WeakPtr<ownership::OwnerSettingsService> & service,bool enabled,bool is_owner)151 void StatsReportingController::SetWithServiceCallback(
152 const base::WeakPtr<ownership::OwnerSettingsService>& service,
153 bool enabled,
154 bool is_owner) {
155 VLOG(1) << "SetWithServiceCallback(" << std::boolalpha << enabled << ")";
156 if (service) // Make sure service wasn't deleted in the meantime.
157 SetWithService(service.get(), enabled);
158 }
159
SetWithService(ownership::OwnerSettingsService * service,bool enabled)160 void StatsReportingController::SetWithService(
161 ownership::OwnerSettingsService* service, // Can be null for non-owners.
162 bool enabled) {
163 VLOG(1) << "SetWithService(" << std::boolalpha << enabled << ")";
164 if (service && service->IsOwner()) {
165 service->SetBoolean(kStatsReportingPref, enabled);
166 ClearPendingValue();
167 NotifyObservers();
168 } else {
169 // Do nothing since we are not the owner.
170 LOG(WARNING) << "Changing settings from non-owner, setting="
171 << kStatsReportingPref;
172 }
173 }
174
NotifyObservers()175 void StatsReportingController::NotifyObservers() {
176 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
177 bool current_value = IsEnabled();
178 VLOG(1) << "Maybe notifying observers of " << std::boolalpha << current_value;
179 if (current_value != value_notified_to_observers_) {
180 VLOG(1) << "Notifying observers";
181 value_notified_to_observers_ = current_value;
182 callback_list_.Notify();
183 } else {
184 VLOG(1) << "Not notifying (already notified)";
185 }
186 }
187
188 DeviceSettingsService::OwnershipStatus
GetOwnershipStatus()189 StatsReportingController::GetOwnershipStatus() {
190 return DeviceSettingsService::Get()->GetOwnershipStatus();
191 }
192
193 ownership::OwnerSettingsService*
GetOwnerSettingsService(Profile * profile)194 StatsReportingController::GetOwnerSettingsService(Profile* profile) {
195 return OwnerSettingsServiceChromeOSFactory::GetForBrowserContext(profile);
196 }
197
GetPendingValue(bool * result)198 bool StatsReportingController::GetPendingValue(bool* result) {
199 if (local_state_->HasPrefPath(kPendingPref)) {
200 *result = local_state_->GetBoolean(kPendingPref);
201 return true;
202 }
203 return false;
204 }
205
ClearPendingValue()206 void StatsReportingController::ClearPendingValue() {
207 VLOG(1) << "ClearPendingValue";
208 local_state_->ClearPref(kPendingPref);
209 }
210
GetSignedStoredValue(bool * result)211 bool StatsReportingController::GetSignedStoredValue(bool* result) {
212 return CrosSettings::Get()->GetBoolean(kStatsReportingPref, result);
213 }
214
215 } // namespace chromeos
216