1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/dom/system/gonk/TimeZoneSettingObserver.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,248 @@ 1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this file, 1.6 + * You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.7 + 1.8 +#include "base/message_loop.h" 1.9 +#include "jsapi.h" 1.10 +#include "mozilla/Attributes.h" 1.11 +#include "mozilla/ClearOnShutdown.h" 1.12 +#include "mozilla/Hal.h" 1.13 +#include "mozilla/Services.h" 1.14 +#include "mozilla/StaticPtr.h" 1.15 +#include "nsCOMPtr.h" 1.16 +#include "nsDebug.h" 1.17 +#include "nsIObserver.h" 1.18 +#include "nsIObserverService.h" 1.19 +#include "nsISettingsService.h" 1.20 +#include "nsJSUtils.h" 1.21 +#include "nsServiceManagerUtils.h" 1.22 +#include "nsString.h" 1.23 +#include "TimeZoneSettingObserver.h" 1.24 +#include "xpcpublic.h" 1.25 +#include "nsContentUtils.h" 1.26 +#include "nsCxPusher.h" 1.27 +#include "nsPrintfCString.h" 1.28 + 1.29 +#undef LOG 1.30 +#define LOG(args...) __android_log_print(ANDROID_LOG_INFO, "Time Zone Setting" , ## args) 1.31 +#define ERR(args...) __android_log_print(ANDROID_LOG_ERROR, "Time Zone Setting" , ## args) 1.32 + 1.33 +#define TIME_TIMEZONE "time.timezone" 1.34 +#define MOZSETTINGS_CHANGED "mozsettings-changed" 1.35 + 1.36 +using namespace mozilla; 1.37 + 1.38 +namespace { 1.39 + 1.40 +class TimeZoneSettingObserver : public nsIObserver 1.41 +{ 1.42 +public: 1.43 + NS_DECL_ISUPPORTS 1.44 + NS_DECL_NSIOBSERVER 1.45 + 1.46 + TimeZoneSettingObserver(); 1.47 + virtual ~TimeZoneSettingObserver(); 1.48 + static nsresult SetTimeZone(const JS::Value &aValue, JSContext *aContext); 1.49 +}; 1.50 + 1.51 +class TimeZoneSettingCb MOZ_FINAL : public nsISettingsServiceCallback 1.52 +{ 1.53 +public: 1.54 + NS_DECL_ISUPPORTS 1.55 + 1.56 + TimeZoneSettingCb() {} 1.57 + 1.58 + NS_IMETHOD Handle(const nsAString &aName, JS::Handle<JS::Value> aResult) { 1.59 + 1.60 + JSContext *cx = nsContentUtils::GetCurrentJSContext(); 1.61 + NS_ENSURE_TRUE(cx, NS_OK); 1.62 + 1.63 + // If we don't have time.timezone value in the settings, we need 1.64 + // to initialize the settings based on the current system timezone 1.65 + // to make settings consistent with system. This usually happens 1.66 + // at the very first boot. After that, settings must have a value. 1.67 + if (aResult.isNull()) { 1.68 + // Get the current system time zone offset. Note that we need to 1.69 + // convert the value to a UTC representation in the format of 1.70 + // "UTC{+,-}hh:mm", so that the Gaia end can know how to interpret. 1.71 + // E.g., -480 is "UTC+08:00"; 630 is "UTC-10:30". 1.72 + int32_t timeZoneOffset = hal::GetTimezoneOffset(); 1.73 + nsPrintfCString curTimeZone("UTC%+03d:%02d", 1.74 + -timeZoneOffset / 60, 1.75 + abs(timeZoneOffset) % 60); 1.76 + 1.77 + // Convert it to a JS string. 1.78 + NS_ConvertUTF8toUTF16 utf16Str(curTimeZone); 1.79 + 1.80 + JS::Rooted<JSString*> jsStr(cx, JS_NewUCStringCopyN(cx, 1.81 + utf16Str.get(), 1.82 + utf16Str.Length())); 1.83 + if (!jsStr) { 1.84 + return NS_ERROR_OUT_OF_MEMORY; 1.85 + } 1.86 + 1.87 + // Set the settings based on the current system timezone. 1.88 + nsCOMPtr<nsISettingsServiceLock> lock; 1.89 + nsCOMPtr<nsISettingsService> settingsService = 1.90 + do_GetService("@mozilla.org/settingsService;1"); 1.91 + if (!settingsService) { 1.92 + ERR("Failed to get settingsLock service!"); 1.93 + return NS_OK; 1.94 + } 1.95 + settingsService->CreateLock(nullptr, getter_AddRefs(lock)); 1.96 + JS::Rooted<JS::Value> value(cx, JS::StringValue(jsStr)); 1.97 + lock->Set(TIME_TIMEZONE, value, nullptr, nullptr); 1.98 + return NS_OK; 1.99 + } 1.100 + 1.101 + // Set the system timezone based on the current settings. 1.102 + if (aResult.isString()) { 1.103 + return TimeZoneSettingObserver::SetTimeZone(aResult, cx); 1.104 + } 1.105 + 1.106 + return NS_OK; 1.107 + } 1.108 + 1.109 + NS_IMETHOD HandleError(const nsAString &aName) { 1.110 + ERR("TimeZoneSettingCb::HandleError: %s\n", NS_LossyConvertUTF16toASCII(aName).get()); 1.111 + return NS_OK; 1.112 + } 1.113 +}; 1.114 + 1.115 +NS_IMPL_ISUPPORTS(TimeZoneSettingCb, nsISettingsServiceCallback) 1.116 + 1.117 +TimeZoneSettingObserver::TimeZoneSettingObserver() 1.118 +{ 1.119 + // Setup an observer to watch changes to the setting. 1.120 + nsCOMPtr<nsIObserverService> observerService = services::GetObserverService(); 1.121 + if (!observerService) { 1.122 + ERR("GetObserverService failed"); 1.123 + return; 1.124 + } 1.125 + nsresult rv; 1.126 + rv = observerService->AddObserver(this, MOZSETTINGS_CHANGED, false); 1.127 + if (NS_FAILED(rv)) { 1.128 + ERR("AddObserver failed"); 1.129 + return; 1.130 + } 1.131 + 1.132 + // Read the 'time.timezone' setting in order to start with a known 1.133 + // value at boot time. The handle() will be called after reading. 1.134 + nsCOMPtr<nsISettingsServiceLock> lock; 1.135 + nsCOMPtr<nsISettingsService> settingsService = 1.136 + do_GetService("@mozilla.org/settingsService;1"); 1.137 + if (!settingsService) { 1.138 + ERR("Failed to get settingsLock service!"); 1.139 + return; 1.140 + } 1.141 + settingsService->CreateLock(nullptr, getter_AddRefs(lock)); 1.142 + nsCOMPtr<nsISettingsServiceCallback> callback = new TimeZoneSettingCb(); 1.143 + lock->Get(TIME_TIMEZONE, callback); 1.144 +} 1.145 + 1.146 +nsresult TimeZoneSettingObserver::SetTimeZone(const JS::Value &aValue, JSContext *aContext) 1.147 +{ 1.148 + // Convert the JS value to a nsCString type. 1.149 + // The value should be a JS string like "America/Chicago" or "UTC-05:00". 1.150 + nsDependentJSString valueStr; 1.151 + if (!valueStr.init(aContext, aValue.toString())) { 1.152 + ERR("Failed to convert JS value to nsCString"); 1.153 + return NS_ERROR_FAILURE; 1.154 + } 1.155 + NS_ConvertUTF16toUTF8 newTimezone(valueStr); 1.156 + 1.157 + // Hal expects opposite sign from general notations, 1.158 + // so we need to flip it. 1.159 + if (newTimezone.Find(NS_LITERAL_CSTRING("UTC+")) == 0) { 1.160 + if (!newTimezone.SetCharAt('-', 3)) { 1.161 + return NS_ERROR_FAILURE; 1.162 + } 1.163 + } else if (newTimezone.Find(NS_LITERAL_CSTRING("UTC-")) == 0) { 1.164 + if (!newTimezone.SetCharAt('+', 3)) { 1.165 + return NS_ERROR_FAILURE; 1.166 + } 1.167 + } 1.168 + 1.169 + // Set the timezone only when the system timezone is not identical. 1.170 + nsCString curTimezone = hal::GetTimezone(); 1.171 + if (!curTimezone.Equals(newTimezone)) { 1.172 + hal::SetTimezone(newTimezone); 1.173 + } 1.174 + 1.175 + return NS_OK; 1.176 +} 1.177 + 1.178 +TimeZoneSettingObserver::~TimeZoneSettingObserver() 1.179 +{ 1.180 + nsCOMPtr<nsIObserverService> observerService = services::GetObserverService(); 1.181 + if (observerService) { 1.182 + observerService->RemoveObserver(this, MOZSETTINGS_CHANGED); 1.183 + } 1.184 +} 1.185 + 1.186 +NS_IMPL_ISUPPORTS(TimeZoneSettingObserver, nsIObserver) 1.187 + 1.188 +NS_IMETHODIMP 1.189 +TimeZoneSettingObserver::Observe(nsISupports *aSubject, 1.190 + const char *aTopic, 1.191 + const char16_t *aData) 1.192 +{ 1.193 + if (strcmp(aTopic, MOZSETTINGS_CHANGED) != 0) { 1.194 + return NS_OK; 1.195 + } 1.196 + 1.197 + // Note that this function gets called for any and all settings changes, 1.198 + // so we need to carefully check if we have the one we're interested in. 1.199 + // 1.200 + // The string that we're interested in will be a JSON string that looks like: 1.201 + // {"key":"time.timezone","value":"America/Chicago"} or 1.202 + // {"key":"time.timezone","value":"UTC-05:00"} 1.203 + 1.204 + AutoSafeJSContext cx; 1.205 + 1.206 + // Parse the JSON value. 1.207 + nsDependentString dataStr(aData); 1.208 + JS::Rooted<JS::Value> val(cx); 1.209 + if (!JS_ParseJSON(cx, dataStr.get(), dataStr.Length(), &val) || 1.210 + !val.isObject()) { 1.211 + return NS_OK; 1.212 + } 1.213 + 1.214 + // Get the key, which should be the JS string "time.timezone". 1.215 + JS::Rooted<JSObject*> obj(cx, &val.toObject()); 1.216 + JS::Rooted<JS::Value> key(cx); 1.217 + if (!JS_GetProperty(cx, obj, "key", &key) || 1.218 + !key.isString()) { 1.219 + return NS_OK; 1.220 + } 1.221 + bool match; 1.222 + if (!JS_StringEqualsAscii(cx, key.toString(), TIME_TIMEZONE, &match) || 1.223 + !match) { 1.224 + return NS_OK; 1.225 + } 1.226 + 1.227 + // Get the value, which should be a JS string like "America/Chicago". 1.228 + JS::Rooted<JS::Value> value(cx); 1.229 + if (!JS_GetProperty(cx, obj, "value", &value) || 1.230 + !value.isString()) { 1.231 + return NS_OK; 1.232 + } 1.233 + 1.234 + // Set the system timezone. 1.235 + return SetTimeZone(value, cx); 1.236 +} 1.237 + 1.238 +} // anonymous namespace 1.239 + 1.240 +static mozilla::StaticRefPtr<TimeZoneSettingObserver> sTimeZoneSettingObserver; 1.241 +namespace mozilla { 1.242 +namespace system { 1.243 +void 1.244 +InitializeTimeZoneSettingObserver() 1.245 +{ 1.246 + sTimeZoneSettingObserver = new TimeZoneSettingObserver(); 1.247 + ClearOnShutdown(&sTimeZoneSettingObserver); 1.248 +} 1.249 + 1.250 +} // namespace system 1.251 +} // namespace mozilla