summaryrefslogtreecommitdiffstats
path: root/dom/system/gonk/TimeZoneSettingObserver.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'dom/system/gonk/TimeZoneSettingObserver.cpp')
-rw-r--r--dom/system/gonk/TimeZoneSettingObserver.cpp239
1 files changed, 239 insertions, 0 deletions
diff --git a/dom/system/gonk/TimeZoneSettingObserver.cpp b/dom/system/gonk/TimeZoneSettingObserver.cpp
new file mode 100644
index 000000000..512f79908
--- /dev/null
+++ b/dom/system/gonk/TimeZoneSettingObserver.cpp
@@ -0,0 +1,239 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "base/message_loop.h"
+#include "jsapi.h"
+#include "mozilla/dom/ScriptSettings.h"
+#include "mozilla/Attributes.h"
+#include "mozilla/ClearOnShutdown.h"
+#include "mozilla/Hal.h"
+#include "mozilla/Services.h"
+#include "mozilla/StaticPtr.h"
+#include "nsCOMPtr.h"
+#include "nsDebug.h"
+#include "nsIObserver.h"
+#include "nsIObserverService.h"
+#include "nsISettingsService.h"
+#include "nsJSUtils.h"
+#include "nsServiceManagerUtils.h"
+#include "nsString.h"
+#include "TimeZoneSettingObserver.h"
+#include "xpcpublic.h"
+#include "nsContentUtils.h"
+#include "nsPrintfCString.h"
+#include "mozilla/dom/BindingUtils.h"
+#include "mozilla/dom/SettingChangeNotificationBinding.h"
+
+#undef LOG
+#undef ERR
+#define LOG(args...) __android_log_print(ANDROID_LOG_INFO, "Time Zone Setting" , ## args)
+#define ERR(args...) __android_log_print(ANDROID_LOG_ERROR, "Time Zone Setting" , ## args)
+
+#define TIME_TIMEZONE "time.timezone"
+#define MOZSETTINGS_CHANGED "mozsettings-changed"
+
+using namespace mozilla;
+using namespace mozilla::dom;
+
+namespace {
+
+class TimeZoneSettingObserver : public nsIObserver
+{
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIOBSERVER
+
+ TimeZoneSettingObserver();
+ static nsresult SetTimeZone(const JS::Value &aValue, JSContext *aContext);
+
+protected:
+ virtual ~TimeZoneSettingObserver();
+};
+
+class TimeZoneSettingCb final : public nsISettingsServiceCallback
+{
+public:
+ NS_DECL_ISUPPORTS
+
+ TimeZoneSettingCb() {}
+
+ NS_IMETHOD Handle(const nsAString &aName, JS::Handle<JS::Value> aResult) {
+
+ JSContext *cx = nsContentUtils::GetCurrentJSContext();
+ NS_ENSURE_TRUE(cx, NS_OK);
+
+ // If we don't have time.timezone value in the settings, we need
+ // to initialize the settings based on the current system timezone
+ // to make settings consistent with system. This usually happens
+ // at the very first boot. After that, settings must have a value.
+ if (aResult.isNull()) {
+ // Get the current system time zone offset. Note that we need to
+ // convert the value to a UTC representation in the format of
+ // "UTC{+,-}hh:mm", so that the Gaia end can know how to interpret.
+ // E.g., -480 is "UTC+08:00"; 630 is "UTC-10:30".
+ int32_t timeZoneOffset = hal::GetTimezoneOffset();
+ nsPrintfCString curTimeZone("UTC%+03d:%02d",
+ -timeZoneOffset / 60,
+ abs(timeZoneOffset) % 60);
+
+ // Convert it to a JS string.
+ NS_ConvertUTF8toUTF16 utf16Str(curTimeZone);
+
+ JS::Rooted<JSString*> jsStr(cx, JS_NewUCStringCopyN(cx,
+ utf16Str.get(),
+ utf16Str.Length()));
+ if (!jsStr) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ // Set the settings based on the current system timezone.
+ nsCOMPtr<nsISettingsServiceLock> lock;
+ nsCOMPtr<nsISettingsService> settingsService =
+ do_GetService("@mozilla.org/settingsService;1");
+ if (!settingsService) {
+ ERR("Failed to get settingsLock service!");
+ return NS_OK;
+ }
+ settingsService->CreateLock(nullptr, getter_AddRefs(lock));
+ JS::Rooted<JS::Value> value(cx, JS::StringValue(jsStr));
+ lock->Set(TIME_TIMEZONE, value, nullptr, nullptr);
+ return NS_OK;
+ }
+
+ // Set the system timezone based on the current settings.
+ if (aResult.isString()) {
+ return TimeZoneSettingObserver::SetTimeZone(aResult, cx);
+ }
+
+ return NS_OK;
+ }
+
+ NS_IMETHOD HandleError(const nsAString &aName) {
+ ERR("TimeZoneSettingCb::HandleError: %s\n", NS_LossyConvertUTF16toASCII(aName).get());
+ return NS_OK;
+ }
+
+protected:
+ ~TimeZoneSettingCb() {}
+};
+
+NS_IMPL_ISUPPORTS(TimeZoneSettingCb, nsISettingsServiceCallback)
+
+TimeZoneSettingObserver::TimeZoneSettingObserver()
+{
+ // Setup an observer to watch changes to the setting.
+ nsCOMPtr<nsIObserverService> observerService = services::GetObserverService();
+ if (!observerService) {
+ ERR("GetObserverService failed");
+ return;
+ }
+ nsresult rv;
+ rv = observerService->AddObserver(this, MOZSETTINGS_CHANGED, false);
+ if (NS_FAILED(rv)) {
+ ERR("AddObserver failed");
+ return;
+ }
+
+ // Read the 'time.timezone' setting in order to start with a known
+ // value at boot time. The handle() will be called after reading.
+ nsCOMPtr<nsISettingsServiceLock> lock;
+ nsCOMPtr<nsISettingsService> settingsService =
+ do_GetService("@mozilla.org/settingsService;1");
+ if (!settingsService) {
+ ERR("Failed to get settingsLock service!");
+ return;
+ }
+ settingsService->CreateLock(nullptr, getter_AddRefs(lock));
+ nsCOMPtr<nsISettingsServiceCallback> callback = new TimeZoneSettingCb();
+ lock->Get(TIME_TIMEZONE, callback);
+}
+
+nsresult TimeZoneSettingObserver::SetTimeZone(const JS::Value &aValue, JSContext *aContext)
+{
+ // Convert the JS value to a nsCString type.
+ // The value should be a JS string like "America/Chicago" or "UTC-05:00".
+ nsAutoJSString valueStr;
+ if (!valueStr.init(aContext, aValue.toString())) {
+ ERR("Failed to convert JS value to nsCString");
+ return NS_ERROR_FAILURE;
+ }
+ NS_ConvertUTF16toUTF8 newTimezone(valueStr);
+
+ // Hal expects opposite sign from general notations,
+ // so we need to flip it.
+ if (newTimezone.Find(NS_LITERAL_CSTRING("UTC+")) == 0) {
+ if (!newTimezone.SetCharAt('-', 3)) {
+ return NS_ERROR_FAILURE;
+ }
+ } else if (newTimezone.Find(NS_LITERAL_CSTRING("UTC-")) == 0) {
+ if (!newTimezone.SetCharAt('+', 3)) {
+ return NS_ERROR_FAILURE;
+ }
+ }
+
+ // Set the timezone only when the system timezone is not identical.
+ nsCString curTimezone = hal::GetTimezone();
+ if (!curTimezone.Equals(newTimezone)) {
+ hal::SetTimezone(newTimezone);
+ }
+
+ return NS_OK;
+}
+
+TimeZoneSettingObserver::~TimeZoneSettingObserver()
+{
+ nsCOMPtr<nsIObserverService> observerService = services::GetObserverService();
+ if (observerService) {
+ observerService->RemoveObserver(this, MOZSETTINGS_CHANGED);
+ }
+}
+
+NS_IMPL_ISUPPORTS(TimeZoneSettingObserver, nsIObserver)
+
+NS_IMETHODIMP
+TimeZoneSettingObserver::Observe(nsISupports *aSubject,
+ const char *aTopic,
+ const char16_t *aData)
+{
+ if (strcmp(aTopic, MOZSETTINGS_CHANGED) != 0) {
+ return NS_OK;
+ }
+
+ // Note that this function gets called for any and all settings changes,
+ // so we need to carefully check if we have the one we're interested in.
+ //
+ // The string that we're interested in will be a JSON string that looks like:
+ // {"key":"time.timezone","value":"America/Chicago"} or
+ // {"key":"time.timezone","value":"UTC-05:00"}
+
+ AutoSafeJSContext cx;
+ RootedDictionary<SettingChangeNotification> setting(cx);
+ if (!WrappedJSToDictionary(cx, aSubject, setting)) {
+ return NS_OK;
+ }
+ if (!setting.mKey.EqualsASCII(TIME_TIMEZONE)) {
+ return NS_OK;
+ }
+ if (!setting.mValue.isString()) {
+ return NS_OK;
+ }
+
+ // Set the system timezone.
+ return SetTimeZone(setting.mValue, cx);
+}
+
+} // namespace
+
+static mozilla::StaticRefPtr<TimeZoneSettingObserver> sTimeZoneSettingObserver;
+namespace mozilla {
+namespace system {
+void
+InitializeTimeZoneSettingObserver()
+{
+ sTimeZoneSettingObserver = new TimeZoneSettingObserver();
+ ClearOnShutdown(&sTimeZoneSettingObserver);
+}
+
+} // namespace system
+} // namespace mozilla