Make sure account reconcilor only works for connected profiles.
[chromium-blink-merge.git] / chromeos / settings / timezone_settings.cc
blob1adcf13c148fcd66899815dbaf989f46829446c4
1 // Copyright 2013 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.
5 #include "chromeos/settings/timezone_settings.h"
7 #include <string>
9 #include "base/bind.h"
10 #include "base/file_util.h"
11 #include "base/files/file_path.h"
12 #include "base/location.h"
13 #include "base/logging.h"
14 #include "base/memory/scoped_ptr.h"
15 #include "base/memory/singleton.h"
16 #include "base/observer_list.h"
17 #include "base/stl_util.h"
18 #include "base/strings/string_util.h"
19 #include "base/strings/utf_string_conversions.h"
20 #include "base/sys_info.h"
21 #include "base/task_runner.h"
22 #include "base/threading/worker_pool.h"
23 #include "third_party/icu/source/i18n/unicode/timezone.h"
25 namespace {
27 // The filepath to the timezone file that symlinks to the actual timezone file.
28 const char kTimezoneSymlink[] = "/var/lib/timezone/localtime";
29 const char kTimezoneSymlink2[] = "/var/lib/timezone/localtime2";
31 // The directory that contains all the timezone files. So for timezone
32 // "US/Pacific", the actual timezone file is: "/usr/share/zoneinfo/US/Pacific"
33 const char kTimezoneFilesDir[] = "/usr/share/zoneinfo/";
35 // Fallback time zone ID used in case of an unexpected error.
36 const char kFallbackTimeZoneId[] = "America/Los_Angeles";
38 // TODO(jungshik): Using Enumerate method in ICU gives 600+ timezones.
39 // Even after filtering out duplicate entries with a strict identity check,
40 // we still have 400+ zones. Relaxing the criteria for the timezone
41 // identity is likely to cut down the number to < 100. Until we
42 // come up with a better list, we hard-code the following list. It came from
43 // from Android initially, but more entries have been added.
44 static const char* kTimeZones[] = {
45 "Pacific/Midway",
46 "Pacific/Honolulu",
47 "America/Anchorage",
48 "America/Los_Angeles",
49 "America/Vancouver",
50 "America/Tijuana",
51 "America/Phoenix",
52 "America/Denver",
53 "America/Edmonton",
54 "America/Chihuahua",
55 "America/Regina",
56 "America/Costa_Rica",
57 "America/Chicago",
58 "America/Mexico_City",
59 "America/Winnipeg",
60 "America/Bogota",
61 "America/New_York",
62 "America/Toronto",
63 "America/Caracas",
64 "America/Barbados",
65 "America/Halifax",
66 "America/Manaus",
67 "America/Santiago",
68 "America/St_Johns",
69 "America/Sao_Paulo",
70 "America/Araguaina",
71 "America/Argentina/Buenos_Aires",
72 "America/Argentina/San_Luis",
73 "America/Montevideo",
74 "America/Godthab",
75 "Atlantic/South_Georgia",
76 "Atlantic/Cape_Verde",
77 "Atlantic/Azores",
78 "Africa/Casablanca",
79 "Europe/London",
80 "Europe/Dublin",
81 "Europe/Amsterdam",
82 "Europe/Belgrade",
83 "Europe/Berlin",
84 "Europe/Brussels",
85 "Europe/Madrid",
86 "Europe/Paris",
87 "Europe/Rome",
88 "Europe/Stockholm",
89 "Europe/Sarajevo",
90 "Europe/Vienna",
91 "Europe/Warsaw",
92 "Europe/Zurich",
93 "Africa/Windhoek",
94 "Africa/Lagos",
95 "Africa/Brazzaville",
96 "Africa/Cairo",
97 "Africa/Harare",
98 "Africa/Maputo",
99 "Africa/Johannesburg",
100 "Europe/Helsinki",
101 "Europe/Athens",
102 "Asia/Amman",
103 "Asia/Beirut",
104 "Asia/Jerusalem",
105 "Europe/Minsk",
106 "Asia/Baghdad",
107 "Asia/Riyadh",
108 "Asia/Kuwait",
109 "Africa/Nairobi",
110 "Asia/Tehran",
111 "Europe/Moscow",
112 "Asia/Dubai",
113 "Asia/Tbilisi",
114 "Indian/Mauritius",
115 "Asia/Baku",
116 "Asia/Yerevan",
117 "Asia/Kabul",
118 "Asia/Karachi",
119 "Asia/Ashgabat",
120 "Asia/Oral",
121 "Asia/Calcutta",
122 "Asia/Colombo",
123 "Asia/Katmandu",
124 "Asia/Yekaterinburg",
125 "Asia/Almaty",
126 "Asia/Dhaka",
127 "Asia/Rangoon",
128 "Asia/Bangkok",
129 "Asia/Jakarta",
130 "Asia/Omsk",
131 "Asia/Novosibirsk",
132 "Asia/Shanghai",
133 "Asia/Hong_Kong",
134 "Asia/Kuala_Lumpur",
135 "Asia/Singapore",
136 "Asia/Manila",
137 "Asia/Taipei",
138 "Asia/Makassar",
139 "Asia/Krasnoyarsk",
140 "Australia/Perth",
141 "Australia/Eucla",
142 "Asia/Irkutsk",
143 "Asia/Seoul",
144 "Asia/Tokyo",
145 "Asia/Jayapura",
146 "Australia/Adelaide",
147 "Australia/Darwin",
148 "Australia/Brisbane",
149 "Australia/Hobart",
150 "Australia/Sydney",
151 "Asia/Yakutsk",
152 "Pacific/Guam",
153 "Pacific/Port_Moresby",
154 "Asia/Vladivostok",
155 "Asia/Sakhalin",
156 "Asia/Magadan",
157 "Pacific/Auckland",
158 "Pacific/Fiji",
159 "Pacific/Majuro",
160 "Pacific/Tongatapu",
161 "Pacific/Apia",
162 "Pacific/Kiritimati",
165 std::string GetTimezoneIDAsString() {
166 // Compare with chromiumos/src/platform/init/ui.conf which fixes certain
167 // incorrect states of the timezone symlink on startup. Thus errors occuring
168 // here should be rather contrived.
170 // Look at kTimezoneSymlink, see which timezone we are symlinked to.
171 char buf[256];
172 const ssize_t len = readlink(kTimezoneSymlink, buf,
173 sizeof(buf)-1);
174 if (len == -1) {
175 LOG(ERROR) << "GetTimezoneID: Cannot read timezone symlink "
176 << kTimezoneSymlink;
177 return std::string();
180 std::string timezone(buf, len);
181 // Remove kTimezoneFilesDir from the beginning.
182 if (timezone.find(kTimezoneFilesDir) != 0) {
183 LOG(ERROR) << "GetTimezoneID: Timezone symlink is wrong "
184 << timezone;
185 return std::string();
188 return timezone.substr(strlen(kTimezoneFilesDir));
191 void SetTimezoneIDFromString(const std::string& id) {
192 // Change the kTimezoneSymlink symlink to the path for this timezone.
193 // We want to do this in an atomic way. So we are going to create the symlink
194 // at kTimezoneSymlink2 and then move it to kTimezoneSymlink
196 base::FilePath timezone_symlink(kTimezoneSymlink);
197 base::FilePath timezone_symlink2(kTimezoneSymlink2);
198 base::FilePath timezone_file(kTimezoneFilesDir + id);
200 // Make sure timezone_file exists.
201 if (!base::PathExists(timezone_file)) {
202 LOG(ERROR) << "SetTimezoneID: Cannot find timezone file "
203 << timezone_file.value();
204 return;
207 // Delete old symlink2 if it exists.
208 base::DeleteFile(timezone_symlink2, false);
210 // Create new symlink2.
211 if (symlink(timezone_file.value().c_str(),
212 timezone_symlink2.value().c_str()) == -1) {
213 LOG(ERROR) << "SetTimezoneID: Unable to create symlink "
214 << timezone_symlink2.value() << " to " << timezone_file.value();
215 return;
218 // Move symlink2 to symlink.
219 if (!base::ReplaceFile(timezone_symlink2, timezone_symlink, NULL)) {
220 LOG(ERROR) << "SetTimezoneID: Unable to move symlink "
221 << timezone_symlink2.value() << " to "
222 << timezone_symlink.value();
226 // Common code of the TimezoneSettings implementations.
227 class TimezoneSettingsBaseImpl : public chromeos::system::TimezoneSettings {
228 public:
229 virtual ~TimezoneSettingsBaseImpl();
231 // TimezoneSettings implementation:
232 virtual const icu::TimeZone& GetTimezone() OVERRIDE;
233 virtual string16 GetCurrentTimezoneID() OVERRIDE;
234 virtual void SetTimezoneFromID(const string16& timezone_id) OVERRIDE;
235 virtual void AddObserver(Observer* observer) OVERRIDE;
236 virtual void RemoveObserver(Observer* observer) OVERRIDE;
237 virtual const std::vector<icu::TimeZone*>& GetTimezoneList() const OVERRIDE;
239 protected:
240 TimezoneSettingsBaseImpl();
242 // Returns |timezone| if it is an element of |timezones_|.
243 // Otherwise, returns a timezone from |timezones_|, if such exists, that has
244 // the same rule as the given |timezone|.
245 // Otherwise, returns NULL.
246 // Note multiple timezones with the same time zone offset may exist
247 // e.g.
248 // US/Pacific == America/Los_Angeles
249 const icu::TimeZone* GetKnownTimezoneOrNull(
250 const icu::TimeZone& timezone) const;
252 ObserverList<Observer> observers_;
253 std::vector<icu::TimeZone*> timezones_;
254 scoped_ptr<icu::TimeZone> timezone_;
256 private:
257 DISALLOW_COPY_AND_ASSIGN(TimezoneSettingsBaseImpl);
260 // The TimezoneSettings implementation used in production.
261 class TimezoneSettingsImpl : public TimezoneSettingsBaseImpl {
262 public:
263 // TimezoneSettings implementation:
264 virtual void SetTimezone(const icu::TimeZone& timezone) OVERRIDE;
266 static TimezoneSettingsImpl* GetInstance();
268 private:
269 friend struct DefaultSingletonTraits<TimezoneSettingsImpl>;
271 TimezoneSettingsImpl();
273 DISALLOW_COPY_AND_ASSIGN(TimezoneSettingsImpl);
276 // The stub TimezoneSettings implementation used on Linux desktop.
277 class TimezoneSettingsStubImpl : public TimezoneSettingsBaseImpl {
278 public:
279 // TimezoneSettings implementation:
280 virtual void SetTimezone(const icu::TimeZone& timezone) OVERRIDE;
282 static TimezoneSettingsStubImpl* GetInstance();
284 private:
285 friend struct DefaultSingletonTraits<TimezoneSettingsStubImpl>;
287 TimezoneSettingsStubImpl();
289 DISALLOW_COPY_AND_ASSIGN(TimezoneSettingsStubImpl);
292 TimezoneSettingsBaseImpl::~TimezoneSettingsBaseImpl() {
293 STLDeleteElements(&timezones_);
296 const icu::TimeZone& TimezoneSettingsBaseImpl::GetTimezone() {
297 return *timezone_.get();
300 string16 TimezoneSettingsBaseImpl::GetCurrentTimezoneID() {
301 return chromeos::system::TimezoneSettings::GetTimezoneID(GetTimezone());
304 void TimezoneSettingsBaseImpl::SetTimezoneFromID(const string16& timezone_id) {
305 scoped_ptr<icu::TimeZone> timezone(icu::TimeZone::createTimeZone(
306 icu::UnicodeString(timezone_id.c_str(), timezone_id.size())));
307 SetTimezone(*timezone);
310 void TimezoneSettingsBaseImpl::AddObserver(Observer* observer) {
311 observers_.AddObserver(observer);
314 void TimezoneSettingsBaseImpl::RemoveObserver(Observer* observer) {
315 observers_.RemoveObserver(observer);
318 const std::vector<icu::TimeZone*>&
319 TimezoneSettingsBaseImpl::GetTimezoneList() const {
320 return timezones_;
323 TimezoneSettingsBaseImpl::TimezoneSettingsBaseImpl() {
324 for (size_t i = 0; i < arraysize(kTimeZones); ++i) {
325 timezones_.push_back(icu::TimeZone::createTimeZone(
326 icu::UnicodeString(kTimeZones[i], -1, US_INV)));
330 const icu::TimeZone* TimezoneSettingsBaseImpl::GetKnownTimezoneOrNull(
331 const icu::TimeZone& timezone) const {
332 const icu::TimeZone* known_timezone = NULL;
333 for (std::vector<icu::TimeZone*>::const_iterator iter = timezones_.begin();
334 iter != timezones_.end(); ++iter) {
335 const icu::TimeZone* entry = *iter;
336 if (*entry == timezone)
337 return entry;
338 if (entry->hasSameRules(timezone))
339 known_timezone = entry;
342 // May return NULL if we did not find a matching timezone in our list.
343 return known_timezone;
346 void TimezoneSettingsImpl::SetTimezone(const icu::TimeZone& timezone) {
347 // Replace |timezone| by a known timezone with the same rules. If none exists
348 // go on with |timezone|.
349 const icu::TimeZone* known_timezone = GetKnownTimezoneOrNull(timezone);
350 if (!known_timezone)
351 known_timezone = &timezone;
353 timezone_.reset(known_timezone->clone());
354 std::string id = UTF16ToUTF8(GetTimezoneID(*known_timezone));
355 VLOG(1) << "Setting timezone to " << id;
356 // It's safe to change the timezone config files in the background as the
357 // following operations don't depend on the completion of the config change.
358 base::WorkerPool::GetTaskRunner(true /* task is slow */)->
359 PostTask(FROM_HERE, base::Bind(&SetTimezoneIDFromString, id));
360 icu::TimeZone::setDefault(*known_timezone);
361 FOR_EACH_OBSERVER(Observer, observers_, TimezoneChanged(*known_timezone));
364 // static
365 TimezoneSettingsImpl* TimezoneSettingsImpl::GetInstance() {
366 return Singleton<TimezoneSettingsImpl,
367 DefaultSingletonTraits<TimezoneSettingsImpl> >::get();
370 TimezoneSettingsImpl::TimezoneSettingsImpl() {
371 std::string id = GetTimezoneIDAsString();
372 if (id.empty()) {
373 id = kFallbackTimeZoneId;
374 LOG(ERROR) << "Got an empty string for timezone, default to '" << id;
377 timezone_.reset(icu::TimeZone::createTimeZone(
378 icu::UnicodeString::fromUTF8(id)));
380 // Store a known timezone equivalent to id in |timezone_|.
381 const icu::TimeZone* known_timezone = GetKnownTimezoneOrNull(*timezone_);
382 if (known_timezone != NULL && *known_timezone != *timezone_)
383 // Not necessary to update the filesystem because |known_timezone| has the
384 // same rules.
385 timezone_.reset(known_timezone->clone());
387 icu::TimeZone::setDefault(*timezone_);
388 VLOG(1) << "Timezone initially set to " << id;
391 void TimezoneSettingsStubImpl::SetTimezone(const icu::TimeZone& timezone) {
392 // Replace |timezone| by a known timezone with the same rules. If none exists
393 // go on with |timezone|.
394 const icu::TimeZone* known_timezone = GetKnownTimezoneOrNull(timezone);
395 if (!known_timezone)
396 known_timezone = &timezone;
398 timezone_.reset(known_timezone->clone());
399 icu::TimeZone::setDefault(*known_timezone);
400 FOR_EACH_OBSERVER(Observer, observers_, TimezoneChanged(*known_timezone));
403 // static
404 TimezoneSettingsStubImpl* TimezoneSettingsStubImpl::GetInstance() {
405 return Singleton<TimezoneSettingsStubImpl,
406 DefaultSingletonTraits<TimezoneSettingsStubImpl> >::get();
409 TimezoneSettingsStubImpl::TimezoneSettingsStubImpl() {
410 timezone_.reset(icu::TimeZone::createDefault());
411 const icu::TimeZone* known_timezone = GetKnownTimezoneOrNull(*timezone_);
412 if (known_timezone != NULL && *known_timezone != *timezone_)
413 timezone_.reset(known_timezone->clone());
416 } // namespace
418 namespace chromeos {
419 namespace system {
421 TimezoneSettings::Observer::~Observer() {}
423 // static
424 TimezoneSettings* TimezoneSettings::GetInstance() {
425 if (base::SysInfo::IsRunningOnChromeOS()) {
426 return TimezoneSettingsImpl::GetInstance();
427 } else {
428 return TimezoneSettingsStubImpl::GetInstance();
432 // static
433 string16 TimezoneSettings::GetTimezoneID(const icu::TimeZone& timezone) {
434 icu::UnicodeString id;
435 timezone.getID(id);
436 return string16(id.getBuffer(), id.length());
439 } // namespace system
440 } // namespace chromeos