Roll src/third_party/skia 7b05ff1:dab1f60
[chromium-blink-merge.git] / chromeos / settings / timezone_settings.cc
bloba6833cda05f2b0d95d29a0fc6e5107f72cf23bfd
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/files/file_path.h"
11 #include "base/files/file_util.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 "chromeos/settings/timezone_settings_helper.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/Chihuahua",
53 "America/Denver",
54 "America/Edmonton",
55 "America/Mazatlan",
56 "America/Regina",
57 "America/Costa_Rica",
58 "America/Chicago",
59 "America/Mexico_City",
60 "America/Winnipeg",
61 "Pacific/Easter",
62 "America/Bogota",
63 "America/Lima",
64 "America/New_York",
65 "America/Toronto",
66 "America/Caracas",
67 "America/Barbados",
68 "America/Halifax",
69 "America/Manaus",
70 "America/Santiago",
71 "America/St_Johns",
72 "America/Araguaina",
73 "America/Argentina/Buenos_Aires",
74 "America/Argentina/San_Luis",
75 "America/Sao_Paulo",
76 "America/Montevideo",
77 "America/Godthab",
78 "Atlantic/South_Georgia",
79 "Atlantic/Cape_Verde",
80 "Atlantic/Azores",
81 "Atlantic/Reykjavik",
82 "Atlantic/St_Helena",
83 "Africa/Casablanca",
84 "Atlantic/Faroe",
85 "Europe/Dublin",
86 "Europe/Lisbon",
87 "Europe/London",
88 "Europe/Amsterdam",
89 "Europe/Belgrade",
90 "Europe/Berlin",
91 "Europe/Bratislava",
92 "Europe/Brussels",
93 "Europe/Budapest",
94 "Europe/Copenhagen",
95 "Europe/Ljubljana",
96 "Europe/Madrid",
97 "Europe/Malta",
98 "Europe/Oslo",
99 "Europe/Paris",
100 "Europe/Prague",
101 "Europe/Rome",
102 "Europe/Stockholm",
103 "Europe/Sarajevo",
104 "Europe/Tirane",
105 "Europe/Vaduz",
106 "Europe/Vienna",
107 "Europe/Warsaw",
108 "Europe/Zagreb",
109 "Europe/Zurich",
110 "Africa/Windhoek",
111 "Africa/Lagos",
112 "Africa/Brazzaville",
113 "Africa/Cairo",
114 "Africa/Harare",
115 "Africa/Maputo",
116 "Africa/Johannesburg",
117 "Europe/Kaliningrad",
118 "Europe/Athens",
119 "Europe/Bucharest",
120 "Europe/Chisinau",
121 "Europe/Helsinki",
122 "Europe/Istanbul",
123 "Europe/Kiev",
124 "Europe/Riga",
125 "Europe/Sofia",
126 "Europe/Tallinn",
127 "Europe/Vilnius",
128 "Asia/Amman",
129 "Asia/Beirut",
130 "Asia/Jerusalem",
131 "Africa/Nairobi",
132 "Asia/Baghdad",
133 "Asia/Riyadh",
134 "Asia/Kuwait",
135 "Europe/Minsk",
136 "Europe/Moscow",
137 "Asia/Tehran",
138 "Europe/Samara",
139 "Asia/Dubai",
140 "Asia/Tbilisi",
141 "Indian/Mauritius",
142 "Asia/Baku",
143 "Asia/Yerevan",
144 "Asia/Kabul",
145 "Asia/Karachi",
146 "Asia/Ashgabat",
147 "Asia/Oral",
148 "Asia/Yekaterinburg",
149 "Asia/Calcutta",
150 "Asia/Colombo",
151 "Asia/Katmandu",
152 "Asia/Omsk",
153 "Asia/Almaty",
154 "Asia/Dhaka",
155 "Asia/Novosibirsk",
156 "Asia/Rangoon",
157 "Asia/Bangkok",
158 "Asia/Jakarta",
159 "Asia/Krasnoyarsk",
160 "Asia/Novokuznetsk",
161 "Asia/Ho_Chi_Minh",
162 "Asia/Phnom_Penh",
163 "Asia/Vientiane",
164 "Asia/Shanghai",
165 "Asia/Hong_Kong",
166 "Asia/Kuala_Lumpur",
167 "Asia/Singapore",
168 "Asia/Manila",
169 "Asia/Taipei",
170 "Asia/Ulaanbaatar",
171 "Asia/Makassar",
172 "Asia/Irkutsk",
173 "Asia/Yakutsk",
174 "Australia/Perth",
175 "Australia/Eucla",
176 "Asia/Seoul",
177 "Asia/Tokyo",
178 "Asia/Jayapura",
179 "Asia/Sakhalin",
180 "Asia/Vladivostok",
181 "Asia/Magadan",
182 "Australia/Darwin",
183 "Australia/Adelaide",
184 "Pacific/Guam",
185 "Australia/Brisbane",
186 "Australia/Hobart",
187 "Australia/Sydney",
188 "Asia/Anadyr",
189 "Pacific/Port_Moresby",
190 "Asia/Kamchatka",
191 "Pacific/Fiji",
192 "Pacific/Majuro",
193 "Pacific/Auckland",
194 "Pacific/Tongatapu",
195 "Pacific/Apia",
196 "Pacific/Kiritimati",
199 std::string GetTimezoneIDAsString() {
200 // Compare with chromiumos/src/platform/init/ui.conf which fixes certain
201 // incorrect states of the timezone symlink on startup. Thus errors occuring
202 // here should be rather contrived.
204 // Look at kTimezoneSymlink, see which timezone we are symlinked to.
205 char buf[256];
206 const ssize_t len = readlink(kTimezoneSymlink, buf,
207 sizeof(buf)-1);
208 if (len == -1) {
209 LOG(ERROR) << "GetTimezoneID: Cannot read timezone symlink "
210 << kTimezoneSymlink;
211 return std::string();
214 std::string timezone(buf, len);
215 // Remove kTimezoneFilesDir from the beginning.
216 if (timezone.find(kTimezoneFilesDir) != 0) {
217 LOG(ERROR) << "GetTimezoneID: Timezone symlink is wrong "
218 << timezone;
219 return std::string();
222 return timezone.substr(strlen(kTimezoneFilesDir));
225 void SetTimezoneIDFromString(const std::string& id) {
226 // Change the kTimezoneSymlink symlink to the path for this timezone.
227 // We want to do this in an atomic way. So we are going to create the symlink
228 // at kTimezoneSymlink2 and then move it to kTimezoneSymlink
230 base::FilePath timezone_symlink(kTimezoneSymlink);
231 base::FilePath timezone_symlink2(kTimezoneSymlink2);
232 base::FilePath timezone_file(kTimezoneFilesDir + id);
234 // Make sure timezone_file exists.
235 if (!base::PathExists(timezone_file)) {
236 LOG(ERROR) << "SetTimezoneID: Cannot find timezone file "
237 << timezone_file.value();
238 return;
241 // Delete old symlink2 if it exists.
242 base::DeleteFile(timezone_symlink2, false);
244 // Create new symlink2.
245 if (symlink(timezone_file.value().c_str(),
246 timezone_symlink2.value().c_str()) == -1) {
247 LOG(ERROR) << "SetTimezoneID: Unable to create symlink "
248 << timezone_symlink2.value() << " to " << timezone_file.value();
249 return;
252 // Move symlink2 to symlink.
253 if (!base::ReplaceFile(timezone_symlink2, timezone_symlink, NULL)) {
254 LOG(ERROR) << "SetTimezoneID: Unable to move symlink "
255 << timezone_symlink2.value() << " to "
256 << timezone_symlink.value();
260 // Common code of the TimezoneSettings implementations.
261 class TimezoneSettingsBaseImpl : public chromeos::system::TimezoneSettings {
262 public:
263 ~TimezoneSettingsBaseImpl() override;
265 // TimezoneSettings implementation:
266 const icu::TimeZone& GetTimezone() override;
267 base::string16 GetCurrentTimezoneID() override;
268 void SetTimezoneFromID(const base::string16& timezone_id) override;
269 void AddObserver(Observer* observer) override;
270 void RemoveObserver(Observer* observer) override;
271 const std::vector<icu::TimeZone*>& GetTimezoneList() const override;
273 protected:
274 TimezoneSettingsBaseImpl();
276 // Returns |timezone| if it is an element of |timezones_|.
277 // Otherwise, returns a timezone from |timezones_|, if such exists, that has
278 // the same rule as the given |timezone|.
279 // Otherwise, returns NULL.
280 // Note multiple timezones with the same time zone rules may exist
281 // e.g.
282 // US/Pacific == America/Los_Angeles
283 const icu::TimeZone* GetKnownTimezoneOrNull(
284 const icu::TimeZone& timezone) const;
286 base::ObserverList<Observer> observers_;
287 std::vector<icu::TimeZone*> timezones_;
288 scoped_ptr<icu::TimeZone> timezone_;
290 private:
291 DISALLOW_COPY_AND_ASSIGN(TimezoneSettingsBaseImpl);
294 // The TimezoneSettings implementation used in production.
295 class TimezoneSettingsImpl : public TimezoneSettingsBaseImpl {
296 public:
297 // TimezoneSettings implementation:
298 void SetTimezone(const icu::TimeZone& timezone) override;
300 static TimezoneSettingsImpl* GetInstance();
302 private:
303 friend struct DefaultSingletonTraits<TimezoneSettingsImpl>;
305 TimezoneSettingsImpl();
307 DISALLOW_COPY_AND_ASSIGN(TimezoneSettingsImpl);
310 // The stub TimezoneSettings implementation used on Linux desktop.
311 class TimezoneSettingsStubImpl : public TimezoneSettingsBaseImpl {
312 public:
313 // TimezoneSettings implementation:
314 void SetTimezone(const icu::TimeZone& timezone) override;
316 static TimezoneSettingsStubImpl* GetInstance();
318 private:
319 friend struct DefaultSingletonTraits<TimezoneSettingsStubImpl>;
321 TimezoneSettingsStubImpl();
323 DISALLOW_COPY_AND_ASSIGN(TimezoneSettingsStubImpl);
326 TimezoneSettingsBaseImpl::~TimezoneSettingsBaseImpl() {
327 STLDeleteElements(&timezones_);
330 const icu::TimeZone& TimezoneSettingsBaseImpl::GetTimezone() {
331 return *timezone_.get();
334 base::string16 TimezoneSettingsBaseImpl::GetCurrentTimezoneID() {
335 return chromeos::system::TimezoneSettings::GetTimezoneID(GetTimezone());
338 void TimezoneSettingsBaseImpl::SetTimezoneFromID(
339 const base::string16& timezone_id) {
340 scoped_ptr<icu::TimeZone> timezone(icu::TimeZone::createTimeZone(
341 icu::UnicodeString(timezone_id.c_str(), timezone_id.size())));
342 SetTimezone(*timezone);
345 void TimezoneSettingsBaseImpl::AddObserver(Observer* observer) {
346 observers_.AddObserver(observer);
349 void TimezoneSettingsBaseImpl::RemoveObserver(Observer* observer) {
350 observers_.RemoveObserver(observer);
353 const std::vector<icu::TimeZone*>&
354 TimezoneSettingsBaseImpl::GetTimezoneList() const {
355 return timezones_;
358 TimezoneSettingsBaseImpl::TimezoneSettingsBaseImpl() {
359 for (size_t i = 0; i < arraysize(kTimeZones); ++i) {
360 timezones_.push_back(icu::TimeZone::createTimeZone(
361 icu::UnicodeString(kTimeZones[i], -1, US_INV)));
365 const icu::TimeZone* TimezoneSettingsBaseImpl::GetKnownTimezoneOrNull(
366 const icu::TimeZone& timezone) const {
367 return chromeos::system::GetKnownTimezoneOrNull(timezone, timezones_);
370 void TimezoneSettingsImpl::SetTimezone(const icu::TimeZone& timezone) {
371 // Replace |timezone| by a known timezone with the same rules. If none exists
372 // go on with |timezone|.
373 const icu::TimeZone* known_timezone = GetKnownTimezoneOrNull(timezone);
374 if (!known_timezone)
375 known_timezone = &timezone;
377 timezone_.reset(known_timezone->clone());
378 std::string id = base::UTF16ToUTF8(GetTimezoneID(*known_timezone));
379 VLOG(1) << "Setting timezone to " << id;
380 // It's safe to change the timezone config files in the background as the
381 // following operations don't depend on the completion of the config change.
382 base::WorkerPool::GetTaskRunner(true /* task is slow */)->
383 PostTask(FROM_HERE, base::Bind(&SetTimezoneIDFromString, id));
384 icu::TimeZone::setDefault(*known_timezone);
385 FOR_EACH_OBSERVER(Observer, observers_, TimezoneChanged(*known_timezone));
388 // static
389 TimezoneSettingsImpl* TimezoneSettingsImpl::GetInstance() {
390 return Singleton<TimezoneSettingsImpl,
391 DefaultSingletonTraits<TimezoneSettingsImpl> >::get();
394 TimezoneSettingsImpl::TimezoneSettingsImpl() {
395 std::string id = GetTimezoneIDAsString();
396 if (id.empty()) {
397 id = kFallbackTimeZoneId;
398 LOG(ERROR) << "Got an empty string for timezone, default to '" << id;
401 timezone_.reset(icu::TimeZone::createTimeZone(
402 icu::UnicodeString::fromUTF8(id)));
404 // Store a known timezone equivalent to id in |timezone_|.
405 const icu::TimeZone* known_timezone = GetKnownTimezoneOrNull(*timezone_);
406 if (known_timezone != NULL && *known_timezone != *timezone_)
407 // Not necessary to update the filesystem because |known_timezone| has the
408 // same rules.
409 timezone_.reset(known_timezone->clone());
411 icu::TimeZone::setDefault(*timezone_);
412 VLOG(1) << "Timezone initially set to " << id;
413 icu::UnicodeString resolvedId;
414 std::string resolvedIdStr;
415 timezone_->getID(resolvedId);
416 VLOG(1) << "Timezone initially resolved to "
417 << resolvedId.toUTF8String(resolvedIdStr);
420 void TimezoneSettingsStubImpl::SetTimezone(const icu::TimeZone& timezone) {
421 // Replace |timezone| by a known timezone with the same rules. If none exists
422 // go on with |timezone|.
423 const icu::TimeZone* known_timezone = GetKnownTimezoneOrNull(timezone);
424 if (!known_timezone)
425 known_timezone = &timezone;
427 std::string id = base::UTF16ToUTF8(GetTimezoneID(*known_timezone));
428 VLOG(1) << "Setting timezone to " << id;
429 timezone_.reset(known_timezone->clone());
430 icu::TimeZone::setDefault(*known_timezone);
431 FOR_EACH_OBSERVER(Observer, observers_, TimezoneChanged(*known_timezone));
434 // static
435 TimezoneSettingsStubImpl* TimezoneSettingsStubImpl::GetInstance() {
436 return Singleton<TimezoneSettingsStubImpl,
437 DefaultSingletonTraits<TimezoneSettingsStubImpl> >::get();
440 TimezoneSettingsStubImpl::TimezoneSettingsStubImpl() {
441 timezone_.reset(icu::TimeZone::createDefault());
442 const icu::TimeZone* known_timezone = GetKnownTimezoneOrNull(*timezone_);
443 if (known_timezone != NULL && *known_timezone != *timezone_)
444 timezone_.reset(known_timezone->clone());
447 } // namespace
449 namespace chromeos {
450 namespace system {
452 TimezoneSettings::Observer::~Observer() {}
454 // static
455 TimezoneSettings* TimezoneSettings::GetInstance() {
456 if (base::SysInfo::IsRunningOnChromeOS()) {
457 return TimezoneSettingsImpl::GetInstance();
458 } else {
459 return TimezoneSettingsStubImpl::GetInstance();
463 // static
464 base::string16 TimezoneSettings::GetTimezoneID(const icu::TimeZone& timezone) {
465 icu::UnicodeString id;
466 timezone.getID(id);
467 return base::string16(id.getBuffer(), id.length());
470 } // namespace system
471 } // namespace chromeos