cros_settings.cc revision 7dbb3d5cf0c15f500944d211057644d6a2f37371
1// Copyright (c) 2012 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/cros_settings.h"
6
7#include "base/bind.h"
8#include "base/command_line.h"
9#include "base/logging.h"
10#include "base/stl_util.h"
11#include "base/strings/string_util.h"
12#include "base/values.h"
13#include "chrome/browser/chrome_notification_types.h"
14#include "chrome/browser/chromeos/settings/device_settings_provider.h"
15#include "chrome/browser/chromeos/settings/device_settings_service.h"
16#include "chrome/browser/chromeos/settings/stub_cros_settings_provider.h"
17#include "chrome/browser/chromeos/settings/system_settings_provider.h"
18#include "chromeos/chromeos_switches.h"
19#include "content/public/browser/notification_details.h"
20#include "content/public/browser/notification_source.h"
21#include "content/public/browser/notification_types.h"
22#include "google_apis/gaia/gaia_auth_util.h"
23
24namespace chromeos {
25
26static CrosSettings*  g_cros_settings = NULL;
27
28// static
29void CrosSettings::Initialize() {
30  CHECK(!g_cros_settings);
31  g_cros_settings = new CrosSettings(DeviceSettingsService::Get());
32}
33
34// static
35bool CrosSettings::IsInitialized() {
36  return g_cros_settings;
37}
38
39// static
40void CrosSettings::Shutdown() {
41  DCHECK(g_cros_settings);
42  delete g_cros_settings;
43  g_cros_settings = NULL;
44}
45
46// static
47CrosSettings* CrosSettings::Get() {
48  CHECK(g_cros_settings);
49  return g_cros_settings;
50}
51
52CrosSettings::CrosSettings(DeviceSettingsService* device_settings_service) {
53  CrosSettingsProvider::NotifyObserversCallback notify_cb(
54      base::Bind(&CrosSettings::FireObservers,
55                 // This is safe since |this| is never deleted.
56                 base::Unretained(this)));
57  if (CommandLine::ForCurrentProcess()->HasSwitch(
58          switches::kStubCrosSettings)) {
59    AddSettingsProvider(new StubCrosSettingsProvider(notify_cb));
60  } else {
61    AddSettingsProvider(
62        new DeviceSettingsProvider(notify_cb, device_settings_service));
63  }
64  // System settings are not mocked currently.
65  AddSettingsProvider(new SystemSettingsProvider(notify_cb));
66}
67
68CrosSettings::~CrosSettings() {
69  STLDeleteElements(&providers_);
70  STLDeleteValues(&settings_observers_);
71}
72
73bool CrosSettings::IsCrosSettings(const std::string& path) {
74  return StartsWithASCII(path, kCrosSettingsPrefix, true);
75}
76
77void CrosSettings::Set(const std::string& path, const base::Value& in_value) {
78  DCHECK(CalledOnValidThread());
79  CrosSettingsProvider* provider;
80  provider = GetProvider(path);
81  if (provider)
82    provider->Set(path, in_value);
83}
84
85const base::Value* CrosSettings::GetPref(const std::string& path) const {
86  DCHECK(CalledOnValidThread());
87  CrosSettingsProvider* provider = GetProvider(path);
88  if (provider)
89    return provider->Get(path);
90  NOTREACHED() << path << " preference was not found in the signed settings.";
91  return NULL;
92}
93
94CrosSettingsProvider::TrustedStatus CrosSettings::PrepareTrustedValues(
95    const base::Closure& callback) const {
96  DCHECK(CalledOnValidThread());
97  for (size_t i = 0; i < providers_.size(); ++i) {
98    CrosSettingsProvider::TrustedStatus status =
99        providers_[i]->PrepareTrustedValues(callback);
100    if (status != CrosSettingsProvider::TRUSTED)
101      return status;
102  }
103  return CrosSettingsProvider::TRUSTED;
104}
105
106void CrosSettings::SetBoolean(const std::string& path, bool in_value) {
107  DCHECK(CalledOnValidThread());
108  base::FundamentalValue value(in_value);
109  Set(path, value);
110}
111
112void CrosSettings::SetInteger(const std::string& path, int in_value) {
113  DCHECK(CalledOnValidThread());
114  base::FundamentalValue value(in_value);
115  Set(path, value);
116}
117
118void CrosSettings::SetDouble(const std::string& path, double in_value) {
119  DCHECK(CalledOnValidThread());
120  base::FundamentalValue value(in_value);
121  Set(path, value);
122}
123
124void CrosSettings::SetString(const std::string& path,
125                             const std::string& in_value) {
126  DCHECK(CalledOnValidThread());
127  base::StringValue value(in_value);
128  Set(path, value);
129}
130
131void CrosSettings::AppendToList(const std::string& path,
132                                const base::Value* value) {
133  DCHECK(CalledOnValidThread());
134  const base::Value* old_value = GetPref(path);
135  scoped_ptr<base::Value> new_value(
136      old_value ? old_value->DeepCopy() : new base::ListValue());
137  static_cast<base::ListValue*>(new_value.get())->Append(value->DeepCopy());
138  Set(path, *new_value);
139}
140
141void CrosSettings::RemoveFromList(const std::string& path,
142                                  const base::Value* value) {
143  DCHECK(CalledOnValidThread());
144  const base::Value* old_value = GetPref(path);
145  scoped_ptr<base::Value> new_value(
146      old_value ? old_value->DeepCopy() : new base::ListValue());
147  static_cast<base::ListValue*>(new_value.get())->Remove(*value, NULL);
148  Set(path, *new_value);
149}
150
151bool CrosSettings::GetBoolean(const std::string& path,
152                              bool* bool_value) const {
153  DCHECK(CalledOnValidThread());
154  const base::Value* value = GetPref(path);
155  if (value)
156    return value->GetAsBoolean(bool_value);
157  return false;
158}
159
160bool CrosSettings::GetInteger(const std::string& path,
161                              int* out_value) const {
162  DCHECK(CalledOnValidThread());
163  const base::Value* value = GetPref(path);
164  if (value)
165    return value->GetAsInteger(out_value);
166  return false;
167}
168
169bool CrosSettings::GetDouble(const std::string& path,
170                             double* out_value) const {
171  DCHECK(CalledOnValidThread());
172  const base::Value* value = GetPref(path);
173  if (value)
174    return value->GetAsDouble(out_value);
175  return false;
176}
177
178bool CrosSettings::GetString(const std::string& path,
179                             std::string* out_value) const {
180  DCHECK(CalledOnValidThread());
181  const base::Value* value = GetPref(path);
182  if (value)
183    return value->GetAsString(out_value);
184  return false;
185}
186
187bool CrosSettings::GetList(const std::string& path,
188                           const base::ListValue** out_value) const {
189  DCHECK(CalledOnValidThread());
190  const base::Value* value = GetPref(path);
191  if (value)
192    return value->GetAsList(out_value);
193  return false;
194}
195
196bool CrosSettings::GetDictionary(
197    const std::string& path,
198    const base::DictionaryValue** out_value) const {
199  DCHECK(CalledOnValidThread());
200  const base::Value* value = GetPref(path);
201  if (value)
202    return value->GetAsDictionary(out_value);
203  return false;
204}
205
206bool CrosSettings::FindEmailInList(const std::string& path,
207                                   const std::string& email) const {
208  DCHECK(CalledOnValidThread());
209  std::string canonicalized_email(
210      gaia::CanonicalizeEmail(gaia::SanitizeEmail(email)));
211  std::string wildcard_email;
212  std::string::size_type at_pos = canonicalized_email.find('@');
213  if (at_pos != std::string::npos) {
214    wildcard_email =
215        std::string("*").append(canonicalized_email.substr(at_pos));
216  }
217
218  const base::ListValue* list;
219  if (!GetList(path, &list))
220    return false;
221  for (base::ListValue::const_iterator entry(list->begin());
222       entry != list->end();
223       ++entry) {
224    std::string entry_string;
225    if (!(*entry)->GetAsString(&entry_string)) {
226      NOTREACHED();
227      continue;
228    }
229    std::string canonicalized_entry(
230        gaia::CanonicalizeEmail(gaia::SanitizeEmail(entry_string)));
231
232    if (canonicalized_entry == canonicalized_email ||
233        canonicalized_entry == wildcard_email) {
234      return true;
235    }
236  }
237  return false;
238}
239
240bool CrosSettings::AddSettingsProvider(CrosSettingsProvider* provider) {
241  DCHECK(CalledOnValidThread());
242  providers_.push_back(provider);
243
244  // Allow the provider to notify this object when settings have changed.
245  // Providers instantiated inside this class will have the same callback
246  // passed to their constructor, but doing it here allows for providers
247  // to be instantiated outside this class.
248  CrosSettingsProvider::NotifyObserversCallback notify_cb(
249      base::Bind(&CrosSettings::FireObservers, base::Unretained(this)));
250  provider->SetNotifyObserversCallback(notify_cb);
251  return true;
252}
253
254bool CrosSettings::RemoveSettingsProvider(CrosSettingsProvider* provider) {
255  DCHECK(CalledOnValidThread());
256  std::vector<CrosSettingsProvider*>::iterator it =
257      std::find(providers_.begin(), providers_.end(), provider);
258  if (it != providers_.end()) {
259    providers_.erase(it);
260    return true;
261  }
262  return false;
263}
264
265void CrosSettings::AddSettingsObserver(const char* path,
266                                       content::NotificationObserver* obs) {
267  DCHECK(path);
268  DCHECK(obs);
269  DCHECK(CalledOnValidThread());
270
271  if (!GetProvider(std::string(path))) {
272    NOTREACHED() << "Trying to add an observer for an unregistered setting: "
273                 << path;
274    return;
275  }
276
277  // Get the settings observer list associated with the path.
278  NotificationObserverList* observer_list = NULL;
279  SettingsObserverMap::iterator observer_iterator =
280      settings_observers_.find(path);
281  if (observer_iterator == settings_observers_.end()) {
282    observer_list = new NotificationObserverList;
283    settings_observers_[path] = observer_list;
284  } else {
285    observer_list = observer_iterator->second;
286  }
287
288  // Verify that this observer doesn't already exist.
289  NotificationObserverList::Iterator it(*observer_list);
290  content::NotificationObserver* existing_obs;
291  while ((existing_obs = it.GetNext()) != NULL) {
292    if (existing_obs == obs)
293      return;
294  }
295
296  // Ok, safe to add the pref observer.
297  observer_list->AddObserver(obs);
298}
299
300void CrosSettings::RemoveSettingsObserver(const char* path,
301                                          content::NotificationObserver* obs) {
302  DCHECK(CalledOnValidThread());
303
304  SettingsObserverMap::iterator observer_iterator =
305      settings_observers_.find(path);
306  if (observer_iterator == settings_observers_.end())
307    return;
308
309  NotificationObserverList* observer_list = observer_iterator->second;
310  observer_list->RemoveObserver(obs);
311}
312
313CrosSettingsProvider* CrosSettings::GetProvider(
314    const std::string& path) const {
315  for (size_t i = 0; i < providers_.size(); ++i) {
316    if (providers_[i]->HandlesSetting(path))
317      return providers_[i];
318  }
319  return NULL;
320}
321
322void CrosSettings::FireObservers(const std::string& path) {
323  DCHECK(CalledOnValidThread());
324  SettingsObserverMap::iterator observer_iterator =
325      settings_observers_.find(path);
326  if (observer_iterator == settings_observers_.end())
327    return;
328
329  NotificationObserverList::Iterator it(*(observer_iterator->second));
330  content::NotificationObserver* observer;
331  while ((observer = it.GetNext()) != NULL) {
332    observer->Observe(chrome::NOTIFICATION_SYSTEM_SETTING_CHANGED,
333                      content::Source<CrosSettings>(this),
334                      content::Details<const std::string>(&path));
335  }
336}
337
338ScopedTestCrosSettings::ScopedTestCrosSettings() {
339  CrosSettings::Initialize();
340}
341
342ScopedTestCrosSettings::~ScopedTestCrosSettings() {
343  CrosSettings::Shutdown();
344}
345
346}  // namespace chromeos
347