| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "chrome/browser/chromeos/system/timezone_settings.h" | 5 #include "chrome/browser/chromeos/system/timezone_settings.h" |
| 6 | 6 |
| 7 #include <string> |
| 8 |
| 7 #include "base/bind.h" | 9 #include "base/bind.h" |
| 8 #include "base/chromeos/chromeos_version.h" | 10 #include "base/chromeos/chromeos_version.h" |
| 9 #include "base/file_path.h" | 11 #include "base/file_path.h" |
| 10 #include "base/file_util.h" | 12 #include "base/file_util.h" |
| 11 #include "base/logging.h" | 13 #include "base/logging.h" |
| 12 #include "base/memory/scoped_ptr.h" | 14 #include "base/memory/scoped_ptr.h" |
| 13 #include "base/memory/singleton.h" | 15 #include "base/memory/singleton.h" |
| 14 #include "base/observer_list.h" | 16 #include "base/observer_list.h" |
| 17 #include "base/stl_util.h" |
| 15 #include "base/string_util.h" | 18 #include "base/string_util.h" |
| 16 #include "base/utf_string_conversions.h" | 19 #include "base/utf_string_conversions.h" |
| 17 #include "content/public/browser/browser_thread.h" | 20 #include "content/public/browser/browser_thread.h" |
| 21 #include "unicode/timezone.h" |
| 18 | 22 |
| 19 using content::BrowserThread; | 23 using content::BrowserThread; |
| 20 | 24 |
| 21 namespace chromeos { | |
| 22 namespace system { | |
| 23 | |
| 24 namespace { | 25 namespace { |
| 25 | 26 |
| 26 // The filepath to the timezone file that symlinks to the actual timezone file. | 27 // The filepath to the timezone file that symlinks to the actual timezone file. |
| 27 const char kTimezoneSymlink[] = "/var/lib/timezone/localtime"; | 28 const char kTimezoneSymlink[] = "/var/lib/timezone/localtime"; |
| 28 const char kTimezoneSymlink2[] = "/var/lib/timezone/localtime2"; | 29 const char kTimezoneSymlink2[] = "/var/lib/timezone/localtime2"; |
| 29 | 30 |
| 30 // The directory that contains all the timezone files. So for timezone | 31 // The directory that contains all the timezone files. So for timezone |
| 31 // "US/Pacific", the actual timezone file is: "/usr/share/zoneinfo/US/Pacific" | 32 // "US/Pacific", the actual timezone file is: "/usr/share/zoneinfo/US/Pacific" |
| 32 const char kTimezoneFilesDir[] = "/usr/share/zoneinfo/"; | 33 const char kTimezoneFilesDir[] = "/usr/share/zoneinfo/"; |
| 33 | 34 |
| 34 // Fallback time zone ID used in case of an unexpected error. | 35 // Fallback time zone ID used in case of an unexpected error. |
| 35 const char kFallbackTimeZoneId[] = "America/Los_Angeles"; | 36 const char kFallbackTimeZoneId[] = "America/Los_Angeles"; |
| 36 | 37 |
| 37 } // namespace | 38 // TODO(jungshik): Using Enumerate method in ICU gives 600+ timezones. |
| 38 | 39 // Even after filtering out duplicate entries with a strict identity check, |
| 39 // The TimezoneSettings implementation used in production. | 40 // we still have 400+ zones. Relaxing the criteria for the timezone |
| 40 class TimezoneSettingsImpl : public TimezoneSettings { | 41 // identity is likely to cut down the number to < 100. Until we |
| 41 public: | 42 // come up with a better list, we hard-code the following list as used by |
| 42 // TimezoneSettings implementation: | 43 // Android. |
| 43 virtual const icu::TimeZone& GetTimezone(); | 44 static const char* kTimeZones[] = { |
| 44 virtual void SetTimezone(const icu::TimeZone& timezone); | 45 "Pacific/Majuro", |
| 45 virtual void AddObserver(Observer* observer); | 46 "Pacific/Midway", |
| 46 virtual void RemoveObserver(Observer* observer); | 47 "Pacific/Honolulu", |
| 47 | 48 "America/Anchorage", |
| 48 static TimezoneSettingsImpl* GetInstance(); | 49 "America/Los_Angeles", |
| 49 | 50 "America/Tijuana", |
| 50 private: | 51 "America/Denver", |
| 51 friend struct DefaultSingletonTraits<TimezoneSettingsImpl>; | 52 "America/Phoenix", |
| 52 | 53 "America/Chihuahua", |
| 53 TimezoneSettingsImpl(); | 54 "America/Chicago", |
| 54 | 55 "America/Mexico_City", |
| 55 scoped_ptr<icu::TimeZone> timezone_; | 56 "America/Costa_Rica", |
| 56 ObserverList<Observer> observers_; | 57 "America/Regina", |
| 57 | 58 "America/New_York", |
| 58 DISALLOW_COPY_AND_ASSIGN(TimezoneSettingsImpl); | 59 "America/Bogota", |
| 60 "America/Caracas", |
| 61 "America/Barbados", |
| 62 "America/Manaus", |
| 63 "America/Santiago", |
| 64 "America/St_Johns", |
| 65 "America/Sao_Paulo", |
| 66 "America/Araguaina", |
| 67 "America/Argentina/Buenos_Aires", |
| 68 "America/Godthab", |
| 69 "America/Montevideo", |
| 70 "Atlantic/South_Georgia", |
| 71 "Atlantic/Azores", |
| 72 "Atlantic/Cape_Verde", |
| 73 "Africa/Casablanca", |
| 74 "Europe/London", |
| 75 "Europe/Amsterdam", |
| 76 "Europe/Belgrade", |
| 77 "Europe/Brussels", |
| 78 "Europe/Sarajevo", |
| 79 "Africa/Windhoek", |
| 80 "Africa/Brazzaville", |
| 81 "Asia/Amman", |
| 82 "Europe/Athens", |
| 83 "Asia/Beirut", |
| 84 "Africa/Cairo", |
| 85 "Europe/Helsinki", |
| 86 "Asia/Jerusalem", |
| 87 "Europe/Minsk", |
| 88 "Africa/Harare", |
| 89 "Asia/Baghdad", |
| 90 "Europe/Moscow", |
| 91 "Asia/Kuwait", |
| 92 "Africa/Nairobi", |
| 93 "Asia/Tehran", |
| 94 "Asia/Baku", |
| 95 "Asia/Tbilisi", |
| 96 "Asia/Yerevan", |
| 97 "Asia/Dubai", |
| 98 "Asia/Kabul", |
| 99 "Asia/Karachi", |
| 100 "Asia/Oral", |
| 101 "Asia/Yekaterinburg", |
| 102 "Asia/Calcutta", |
| 103 "Asia/Colombo", |
| 104 "Asia/Katmandu", |
| 105 "Asia/Almaty", |
| 106 "Asia/Rangoon", |
| 107 "Asia/Krasnoyarsk", |
| 108 "Asia/Bangkok", |
| 109 "Asia/Shanghai", |
| 110 "Asia/Hong_Kong", |
| 111 "Asia/Irkutsk", |
| 112 "Asia/Kuala_Lumpur", |
| 113 "Australia/Perth", |
| 114 "Asia/Taipei", |
| 115 "Asia/Seoul", |
| 116 "Asia/Tokyo", |
| 117 "Asia/Yakutsk", |
| 118 "Australia/Adelaide", |
| 119 "Australia/Darwin", |
| 120 "Australia/Brisbane", |
| 121 "Australia/Hobart", |
| 122 "Australia/Sydney", |
| 123 "Asia/Vladivostok", |
| 124 "Pacific/Guam", |
| 125 "Asia/Magadan", |
| 126 "Pacific/Auckland", |
| 127 "Pacific/Fiji", |
| 128 "Pacific/Tongatapu", |
| 59 }; | 129 }; |
| 60 | 130 |
| 61 std::string GetTimezoneIDAsString() { | 131 std::string GetTimezoneIDAsString() { |
| 132 // Compare with chromiumos/src/platform/init/ui.conf which fixes certain |
| 133 // incorrect states of the timezone symlink on startup. Thus errors occuring |
| 134 // here should be rather contrived. |
| 135 |
| 62 // Look at kTimezoneSymlink, see which timezone we are symlinked to. | 136 // Look at kTimezoneSymlink, see which timezone we are symlinked to. |
| 63 char buf[256]; | 137 char buf[256]; |
| 64 const ssize_t len = readlink(kTimezoneSymlink, buf, | 138 const ssize_t len = readlink(kTimezoneSymlink, buf, |
| 65 sizeof(buf)-1); | 139 sizeof(buf)-1); |
| 66 if (len == -1) { | 140 if (len == -1) { |
| 67 LOG(ERROR) << "GetTimezoneID: Cannot read timezone symlink " | 141 LOG(ERROR) << "GetTimezoneID: Cannot read timezone symlink " |
| 68 << kTimezoneSymlink; | 142 << kTimezoneSymlink; |
| 69 return std::string(); | 143 return std::string(); |
| 70 } | 144 } |
| 71 | 145 |
| (...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 108 } | 182 } |
| 109 | 183 |
| 110 // Move symlink2 to symlink. | 184 // Move symlink2 to symlink. |
| 111 if (!file_util::ReplaceFile(timezone_symlink2, timezone_symlink)) { | 185 if (!file_util::ReplaceFile(timezone_symlink2, timezone_symlink)) { |
| 112 LOG(ERROR) << "SetTimezoneID: Unable to move symlink " | 186 LOG(ERROR) << "SetTimezoneID: Unable to move symlink " |
| 113 << timezone_symlink2.value() << " to " | 187 << timezone_symlink2.value() << " to " |
| 114 << timezone_symlink.value(); | 188 << timezone_symlink.value(); |
| 115 } | 189 } |
| 116 } | 190 } |
| 117 | 191 |
| 118 const icu::TimeZone& TimezoneSettingsImpl::GetTimezone() { | 192 // Common code of the TimezoneSettings implementations. |
| 193 class TimezoneSettingsBaseImpl : public chromeos::system::TimezoneSettings { |
| 194 public: |
| 195 virtual ~TimezoneSettingsBaseImpl(); |
| 196 |
| 197 // TimezoneSettings implementation: |
| 198 virtual const icu::TimeZone& GetTimezone() OVERRIDE; |
| 199 virtual string16 GetCurrentTimezoneID() OVERRIDE; |
| 200 virtual void SetTimezoneFromID(const string16& timezone_id) OVERRIDE; |
| 201 virtual void AddObserver(Observer* observer) OVERRIDE; |
| 202 virtual void RemoveObserver(Observer* observer) OVERRIDE; |
| 203 virtual const std::vector<icu::TimeZone*>& GetTimezoneList() const OVERRIDE; |
| 204 |
| 205 protected: |
| 206 TimezoneSettingsBaseImpl(); |
| 207 |
| 208 // Returns |timezone| if it is an element of |timezones_|. |
| 209 // Otherwise, returns a timezone from |timezones_|, if such exists, that has |
| 210 // the same rule as the given |timezone|. |
| 211 // Otherwise, returns NULL. |
| 212 // Note multiple timezones with the same time zone offset may exist |
| 213 // e.g. |
| 214 // US/Pacific == America/Los_Angeles |
| 215 const icu::TimeZone* GetKnownTimezoneOrNull( |
| 216 const icu::TimeZone& timezone) const; |
| 217 |
| 218 ObserverList<Observer> observers_; |
| 219 std::vector<icu::TimeZone*> timezones_; |
| 220 scoped_ptr<icu::TimeZone> timezone_; |
| 221 |
| 222 private: |
| 223 DISALLOW_COPY_AND_ASSIGN(TimezoneSettingsBaseImpl); |
| 224 }; |
| 225 |
| 226 // The TimezoneSettings implementation used in production. |
| 227 class TimezoneSettingsImpl : public TimezoneSettingsBaseImpl { |
| 228 public: |
| 229 // TimezoneSettings implementation: |
| 230 virtual void SetTimezone(const icu::TimeZone& timezone) OVERRIDE; |
| 231 |
| 232 static TimezoneSettingsImpl* GetInstance(); |
| 233 |
| 234 private: |
| 235 friend struct DefaultSingletonTraits<TimezoneSettingsImpl>; |
| 236 |
| 237 TimezoneSettingsImpl(); |
| 238 |
| 239 DISALLOW_COPY_AND_ASSIGN(TimezoneSettingsImpl); |
| 240 }; |
| 241 |
| 242 // The stub TimezoneSettings implementation used on Linux desktop. |
| 243 class TimezoneSettingsStubImpl : public TimezoneSettingsBaseImpl { |
| 244 public: |
| 245 // TimezoneSettings implementation: |
| 246 virtual void SetTimezone(const icu::TimeZone& timezone) OVERRIDE; |
| 247 |
| 248 static TimezoneSettingsStubImpl* GetInstance(); |
| 249 |
| 250 private: |
| 251 friend struct DefaultSingletonTraits<TimezoneSettingsStubImpl>; |
| 252 |
| 253 TimezoneSettingsStubImpl(); |
| 254 |
| 255 DISALLOW_COPY_AND_ASSIGN(TimezoneSettingsStubImpl); |
| 256 }; |
| 257 |
| 258 TimezoneSettingsBaseImpl::~TimezoneSettingsBaseImpl() { |
| 259 STLDeleteElements(&timezones_); |
| 260 } |
| 261 |
| 262 const icu::TimeZone& TimezoneSettingsBaseImpl::GetTimezone() { |
| 119 return *timezone_.get(); | 263 return *timezone_.get(); |
| 120 } | 264 } |
| 121 | 265 |
| 266 string16 TimezoneSettingsBaseImpl::GetCurrentTimezoneID() { |
| 267 return chromeos::system::TimezoneSettings::GetTimezoneID(GetTimezone()); |
| 268 } |
| 269 |
| 270 void TimezoneSettingsBaseImpl::SetTimezoneFromID(const string16& timezone_id) { |
| 271 scoped_ptr<icu::TimeZone> timezone(icu::TimeZone::createTimeZone( |
| 272 icu::UnicodeString(timezone_id.c_str(), timezone_id.size()))); |
| 273 SetTimezone(*timezone); |
| 274 } |
| 275 |
| 276 void TimezoneSettingsBaseImpl::AddObserver(Observer* observer) { |
| 277 observers_.AddObserver(observer); |
| 278 } |
| 279 |
| 280 void TimezoneSettingsBaseImpl::RemoveObserver(Observer* observer) { |
| 281 observers_.RemoveObserver(observer); |
| 282 } |
| 283 |
| 284 const std::vector<icu::TimeZone*>& |
| 285 TimezoneSettingsBaseImpl::GetTimezoneList() const { |
| 286 return timezones_; |
| 287 } |
| 288 |
| 289 TimezoneSettingsBaseImpl::TimezoneSettingsBaseImpl() { |
| 290 for (size_t i = 0; i < arraysize(kTimeZones); ++i) { |
| 291 timezones_.push_back(icu::TimeZone::createTimeZone( |
| 292 icu::UnicodeString(kTimeZones[i], -1, US_INV))); |
| 293 } |
| 294 } |
| 295 |
| 296 const icu::TimeZone* TimezoneSettingsBaseImpl::GetKnownTimezoneOrNull( |
| 297 const icu::TimeZone& timezone) const { |
| 298 const icu::TimeZone* known_timezone = NULL; |
| 299 for (std::vector<icu::TimeZone*>::const_iterator iter = timezones_.begin(); |
| 300 iter != timezones_.end(); ++iter) { |
| 301 const icu::TimeZone* entry = *iter; |
| 302 if (*entry == timezone) |
| 303 return entry; |
| 304 if (entry->hasSameRules(timezone)) |
| 305 known_timezone = entry; |
| 306 } |
| 307 |
| 308 // May return NULL if we did not find a matching timezone in our list. |
| 309 return known_timezone; |
| 310 } |
| 311 |
| 122 void TimezoneSettingsImpl::SetTimezone(const icu::TimeZone& timezone) { | 312 void TimezoneSettingsImpl::SetTimezone(const icu::TimeZone& timezone) { |
| 123 timezone_.reset(timezone.clone()); | 313 // Replace |timezone| by a known timezone with the same rules. If none exists |
| 124 icu::UnicodeString unicode; | 314 // go on with |timezone|. |
| 125 timezone.getID(unicode); | 315 const icu::TimeZone* known_timezone = GetKnownTimezoneOrNull(timezone); |
| 126 std::string id; | 316 if (!known_timezone) |
| 127 UTF16ToUTF8(unicode.getBuffer(), unicode.length(), &id); | 317 known_timezone = &timezone; |
| 318 |
| 319 timezone_.reset(known_timezone->clone()); |
| 320 std::string id = UTF16ToUTF8(GetTimezoneID(*known_timezone)); |
| 128 VLOG(1) << "Setting timezone to " << id; | 321 VLOG(1) << "Setting timezone to " << id; |
| 129 // Change the timezone config files on the FILE thread. It's safe to do this | 322 // It's safe to change the timezone config files in the background as the |
| 130 // in the background as the following operations don't depend on the | 323 // following operations don't depend on the completion of the config change. |
| 131 // completion of the config change. | 324 BrowserThread::PostBlockingPoolTask(FROM_HERE, |
| 132 BrowserThread::PostTask(BrowserThread::FILE, | 325 base::Bind(&SetTimezoneIDFromString, id)); |
| 133 FROM_HERE, | 326 icu::TimeZone::setDefault(*known_timezone); |
| 134 base::Bind(&SetTimezoneIDFromString, id)); | 327 FOR_EACH_OBSERVER(Observer, observers_, TimezoneChanged(*known_timezone)); |
| 135 icu::TimeZone::setDefault(timezone); | 328 } |
| 136 FOR_EACH_OBSERVER(Observer, observers_, TimezoneChanged(timezone)); | 329 |
| 137 } | 330 // static |
| 138 | 331 TimezoneSettingsImpl* TimezoneSettingsImpl::GetInstance() { |
| 139 void TimezoneSettingsImpl::AddObserver(Observer* observer) { | 332 return Singleton<TimezoneSettingsImpl, |
| 140 observers_.AddObserver(observer); | 333 DefaultSingletonTraits<TimezoneSettingsImpl> >::get(); |
| 141 } | |
| 142 | |
| 143 void TimezoneSettingsImpl::RemoveObserver(Observer* observer) { | |
| 144 observers_.RemoveObserver(observer); | |
| 145 } | 334 } |
| 146 | 335 |
| 147 TimezoneSettingsImpl::TimezoneSettingsImpl() { | 336 TimezoneSettingsImpl::TimezoneSettingsImpl() { |
| 148 // Get Timezone | |
| 149 std::string id = GetTimezoneIDAsString(); | 337 std::string id = GetTimezoneIDAsString(); |
| 150 if (id.empty()) { | 338 if (id.empty()) { |
| 151 id = kFallbackTimeZoneId; | 339 id = kFallbackTimeZoneId; |
| 152 LOG(ERROR) << "Got an empty string for timezone, default to " << id; | 340 LOG(ERROR) << "Got an empty string for timezone, default to '" << id; |
| 153 } | 341 } |
| 154 icu::TimeZone* timezone = | 342 |
| 155 icu::TimeZone::createTimeZone(icu::UnicodeString::fromUTF8(id)); | 343 timezone_.reset(icu::TimeZone::createTimeZone( |
| 156 timezone_.reset(timezone); | 344 icu::UnicodeString::fromUTF8(id))); |
| 157 icu::TimeZone::setDefault(*timezone); | 345 |
| 158 VLOG(1) << "Timezone is " << id; | 346 // Store a known timezone equivalent to id in |timezone_|. |
| 159 } | 347 const icu::TimeZone* known_timezone = GetKnownTimezoneOrNull(*timezone_); |
| 160 | 348 if (known_timezone != NULL && *known_timezone != *timezone_) |
| 161 TimezoneSettingsImpl* TimezoneSettingsImpl::GetInstance() { | 349 // Not necessary to update the filesystem because |known_timezone| has the |
| 162 return Singleton<TimezoneSettingsImpl, | 350 // same rules. |
| 163 DefaultSingletonTraits<TimezoneSettingsImpl> >::get(); | 351 timezone_.reset(known_timezone->clone()); |
| 164 } | 352 |
| 165 | 353 icu::TimeZone::setDefault(*timezone_); |
| 166 // The stub TimezoneSettings implementation used on Linux desktop. | 354 VLOG(1) << "Timezone initially set to " << id; |
| 167 class TimezoneSettingsStubImpl : public TimezoneSettings { | 355 } |
| 168 public: | 356 |
| 169 // TimezoneSettings implementation: | 357 void TimezoneSettingsStubImpl::SetTimezone(const icu::TimeZone& timezone) { |
| 170 virtual const icu::TimeZone& GetTimezone() { | 358 // Replace |timezone| by a known timezone with the same rules. If none exists |
| 171 return *timezone_.get(); | 359 // go on with |timezone|. |
| 172 } | 360 const icu::TimeZone* known_timezone = GetKnownTimezoneOrNull(timezone); |
| 173 | 361 if (!known_timezone) |
| 174 virtual void SetTimezone(const icu::TimeZone& timezone) { | 362 known_timezone = &timezone; |
| 175 icu::TimeZone::setDefault(timezone); | 363 |
| 176 FOR_EACH_OBSERVER(Observer, observers_, TimezoneChanged(timezone)); | 364 timezone_.reset(known_timezone->clone()); |
| 177 } | 365 icu::TimeZone::setDefault(*known_timezone); |
| 178 | 366 FOR_EACH_OBSERVER(Observer, observers_, TimezoneChanged(*known_timezone)); |
| 179 virtual void AddObserver(Observer* observer) { | 367 } |
| 180 observers_.AddObserver(observer); | 368 |
| 181 } | 369 // static |
| 182 | 370 TimezoneSettingsStubImpl* TimezoneSettingsStubImpl::GetInstance() { |
| 183 virtual void RemoveObserver(Observer* observer) { | 371 return Singleton<TimezoneSettingsStubImpl, |
| 184 observers_.RemoveObserver(observer); | 372 DefaultSingletonTraits<TimezoneSettingsStubImpl> >::get(); |
| 185 } | 373 } |
| 186 | 374 |
| 187 static TimezoneSettingsStubImpl* GetInstance() { | 375 TimezoneSettingsStubImpl::TimezoneSettingsStubImpl() { |
| 188 return Singleton<TimezoneSettingsStubImpl, | 376 timezone_.reset(icu::TimeZone::createDefault()); |
| 189 DefaultSingletonTraits<TimezoneSettingsStubImpl> >::get(); | 377 const icu::TimeZone* known_timezone = GetKnownTimezoneOrNull(*timezone_); |
| 190 } | 378 if (known_timezone != NULL && *known_timezone != *timezone_) |
| 191 | 379 timezone_.reset(known_timezone->clone()); |
| 192 private: | 380 } |
| 193 friend struct DefaultSingletonTraits<TimezoneSettingsStubImpl>; | 381 |
| 194 | 382 } // namespace |
| 195 TimezoneSettingsStubImpl() { | 383 |
| 196 timezone_.reset(icu::TimeZone::createDefault()); | 384 namespace chromeos { |
| 197 } | 385 namespace system { |
| 198 | 386 |
| 199 scoped_ptr<icu::TimeZone> timezone_; | 387 TimezoneSettings::Observer::~Observer() {} |
| 200 ObserverList<Observer> observers_; | 388 |
| 201 | 389 // static |
| 202 DISALLOW_COPY_AND_ASSIGN(TimezoneSettingsStubImpl); | |
| 203 }; | |
| 204 | |
| 205 TimezoneSettings* TimezoneSettings::GetInstance() { | 390 TimezoneSettings* TimezoneSettings::GetInstance() { |
| 206 if (base::chromeos::IsRunningOnChromeOS()) { | 391 if (base::chromeos::IsRunningOnChromeOS()) { |
| 207 return TimezoneSettingsImpl::GetInstance(); | 392 return TimezoneSettingsImpl::GetInstance(); |
| 208 } else { | 393 } else { |
| 209 return TimezoneSettingsStubImpl::GetInstance(); | 394 return TimezoneSettingsStubImpl::GetInstance(); |
| 210 } | 395 } |
| 211 } | 396 } |
| 212 | 397 |
| 398 // static |
| 399 string16 TimezoneSettings::GetTimezoneID(const icu::TimeZone& timezone) { |
| 400 icu::UnicodeString id; |
| 401 timezone.getID(id); |
| 402 return string16(id.getBuffer(), id.length()); |
| 403 } |
| 404 |
| 213 } // namespace system | 405 } // namespace system |
| 214 } // namespace chromeos | 406 } // namespace chromeos |
| OLD | NEW |