diff options
Diffstat (limited to 'dom/power/PowerManagerService.cpp')
-rw-r--r-- | dom/power/PowerManagerService.cpp | 255 |
1 files changed, 255 insertions, 0 deletions
diff --git a/dom/power/PowerManagerService.cpp b/dom/power/PowerManagerService.cpp new file mode 100644 index 000000000..40110aa67 --- /dev/null +++ b/dom/power/PowerManagerService.cpp @@ -0,0 +1,255 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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 "mozilla/dom/ContentParent.h" +#include "mozilla/Hal.h" +#include "mozilla/HalWakeLock.h" +#include "mozilla/ClearOnShutdown.h" +#include "mozilla/Preferences.h" +#include "mozilla/Services.h" +#include "jsprf.h" +#include "nsIDOMWakeLockListener.h" +#include "nsIDOMWindow.h" +#include "nsIObserverService.h" +#include "PowerManagerService.h" +#include "WakeLock.h" + +// For _exit(). +#ifdef XP_WIN +#include <process.h> +#else +#include <unistd.h> +#endif + +#ifdef ANDROID +#include <android/log.h> +extern "C" char* PrintJSStack(); +static void LogFunctionAndJSStack(const char* funcname) { + char *jsstack = PrintJSStack(); + __android_log_print(ANDROID_LOG_INFO, "PowerManagerService", \ + "Call to %s. The JS stack is:\n%s\n", + funcname, + jsstack ? jsstack : "<no JS stack>"); + JS_smprintf_free(jsstack); +} +// bug 839452 +#define LOG_FUNCTION_AND_JS_STACK() \ + LogFunctionAndJSStack(__PRETTY_FUNCTION__); +#else +#define LOG_FUNCTION_AND_JS_STACK() +#endif + +namespace mozilla { +namespace dom { +namespace power { + +using namespace hal; + +NS_IMPL_ISUPPORTS(PowerManagerService, nsIPowerManagerService) + +/* static */ StaticRefPtr<PowerManagerService> PowerManagerService::sSingleton; + +/* static */ already_AddRefed<PowerManagerService> +PowerManagerService::GetInstance() +{ + if (!sSingleton) { + sSingleton = new PowerManagerService(); + sSingleton->Init(); + ClearOnShutdown(&sSingleton); + } + + RefPtr<PowerManagerService> service = sSingleton.get(); + return service.forget(); +} + +void +PowerManagerService::Init() +{ + RegisterWakeLockObserver(this); + + // NB: default to *enabling* the watchdog even when the pref is + // absent, in case the profile might be damaged and we need to + // restart to repair it. + mWatchdogTimeoutSecs = + Preferences::GetInt("shutdown.watchdog.timeoutSecs", 10); +} + +PowerManagerService::~PowerManagerService() +{ + UnregisterWakeLockObserver(this); +} + +void +PowerManagerService::ComputeWakeLockState(const WakeLockInformation& aWakeLockInfo, + nsAString &aState) +{ + WakeLockState state = hal::ComputeWakeLockState(aWakeLockInfo.numLocks(), + aWakeLockInfo.numHidden()); + switch (state) { + case WAKE_LOCK_STATE_UNLOCKED: + aState.AssignLiteral("unlocked"); + break; + case WAKE_LOCK_STATE_HIDDEN: + aState.AssignLiteral("locked-background"); + break; + case WAKE_LOCK_STATE_VISIBLE: + aState.AssignLiteral("locked-foreground"); + break; + } +} + +void +PowerManagerService::Notify(const WakeLockInformation& aWakeLockInfo) +{ + nsAutoString state; + ComputeWakeLockState(aWakeLockInfo, state); + + /** + * Copy the listeners list before we walk through the callbacks + * because the callbacks may install new listeners. We expect no + * more than one listener per window, so it shouldn't be too long. + */ + AutoTArray<nsCOMPtr<nsIDOMMozWakeLockListener>, 2> listeners(mWakeLockListeners); + + for (uint32_t i = 0; i < listeners.Length(); ++i) { + listeners[i]->Callback(aWakeLockInfo.topic(), state); + } +} + +void +PowerManagerService::SyncProfile() +{ + nsCOMPtr<nsIObserverService> obsServ = services::GetObserverService(); + if (obsServ) { + NS_NAMED_LITERAL_STRING(context, "shutdown-persist"); + obsServ->NotifyObservers(nullptr, "profile-change-net-teardown", context.get()); + obsServ->NotifyObservers(nullptr, "profile-change-teardown", context.get()); + obsServ->NotifyObservers(nullptr, "profile-before-change", context.get()); + obsServ->NotifyObservers(nullptr, "profile-before-change-qm", context.get()); + obsServ->NotifyObservers(nullptr, "profile-before-change-telemetry", context.get()); + } +} + +NS_IMETHODIMP +PowerManagerService::Reboot() +{ + LOG_FUNCTION_AND_JS_STACK() // bug 839452 + + StartForceQuitWatchdog(eHalShutdownMode_Reboot, mWatchdogTimeoutSecs); + // To synchronize any unsaved user data before rebooting. + SyncProfile(); + hal::Reboot(); + MOZ_CRASH("hal::Reboot() shouldn't return"); +} + +NS_IMETHODIMP +PowerManagerService::PowerOff() +{ + LOG_FUNCTION_AND_JS_STACK() // bug 839452 + + StartForceQuitWatchdog(eHalShutdownMode_PowerOff, mWatchdogTimeoutSecs); + // To synchronize any unsaved user data before powering off. + SyncProfile(); + hal::PowerOff(); + MOZ_CRASH("hal::PowerOff() shouldn't return"); +} + +NS_IMETHODIMP +PowerManagerService::Restart() +{ + LOG_FUNCTION_AND_JS_STACK() // bug 839452 + + // FIXME/bug 796826 this implementation is currently gonk-specific, + // because it relies on the Gonk to initialize the Gecko processes to + // restart B2G. It's better to do it here to have a real "restart". + StartForceQuitWatchdog(eHalShutdownMode_Restart, mWatchdogTimeoutSecs); + // Ensure all content processes are dead before we continue + // restarting. This code is used to restart to apply updates, and + // if we don't join all the subprocesses, race conditions can cause + // them to see an inconsistent view of the application directory. + ContentParent::JoinAllSubprocesses(); + + // To synchronize any unsaved user data before restarting. + SyncProfile(); +#ifdef XP_UNIX + sync(); +#endif + _exit(0); + MOZ_CRASH("_exit() shouldn't return"); +} + +NS_IMETHODIMP +PowerManagerService::AddWakeLockListener(nsIDOMMozWakeLockListener *aListener) +{ + if (mWakeLockListeners.Contains(aListener)) + return NS_OK; + + mWakeLockListeners.AppendElement(aListener); + return NS_OK; +} + +NS_IMETHODIMP +PowerManagerService::RemoveWakeLockListener(nsIDOMMozWakeLockListener *aListener) +{ + mWakeLockListeners.RemoveElement(aListener); + return NS_OK; +} + +NS_IMETHODIMP +PowerManagerService::GetWakeLockState(const nsAString &aTopic, nsAString &aState) +{ + WakeLockInformation info; + GetWakeLockInfo(aTopic, &info); + + ComputeWakeLockState(info, aState); + + return NS_OK; +} + +already_AddRefed<WakeLock> +PowerManagerService::NewWakeLock(const nsAString& aTopic, + nsPIDOMWindowInner* aWindow, + mozilla::ErrorResult& aRv) +{ + RefPtr<WakeLock> wakelock = new WakeLock(); + aRv = wakelock->Init(aTopic, aWindow); + if (aRv.Failed()) { + return nullptr; + } + + return wakelock.forget(); +} + +NS_IMETHODIMP +PowerManagerService::NewWakeLock(const nsAString &aTopic, + mozIDOMWindow *aWindow, + nsISupports **aWakeLock) +{ + mozilla::ErrorResult rv; + RefPtr<WakeLock> wakelock = + NewWakeLock(aTopic, nsPIDOMWindowInner::From(aWindow), rv); + if (rv.Failed()) { + return rv.StealNSResult(); + } + + nsCOMPtr<nsIDOMEventListener> eventListener = wakelock.get(); + eventListener.forget(aWakeLock); + return NS_OK; +} + +already_AddRefed<WakeLock> +PowerManagerService::NewWakeLockOnBehalfOfProcess(const nsAString& aTopic, + ContentParent* aContentParent) +{ + RefPtr<WakeLock> wakelock = new WakeLock(); + nsresult rv = wakelock->Init(aTopic, aContentParent); + NS_ENSURE_SUCCESS(rv, nullptr); + return wakelock.forget(); +} + +} // namespace power +} // namespace dom +} // namespace mozilla |