michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "mozilla/dom/ContentParent.h" michael@0: #include "mozilla/Hal.h" michael@0: #include "mozilla/HalWakeLock.h" michael@0: #include "mozilla/ClearOnShutdown.h" michael@0: #include "mozilla/Preferences.h" michael@0: #include "mozilla/Services.h" michael@0: #include "nsIDOMWakeLockListener.h" michael@0: #include "nsIDOMWindow.h" michael@0: #include "nsIObserverService.h" michael@0: #include "PowerManagerService.h" michael@0: #include "WakeLock.h" michael@0: michael@0: // For _exit(). michael@0: #ifdef XP_WIN michael@0: #include michael@0: #else michael@0: #include michael@0: #endif michael@0: michael@0: #ifdef ANDROID michael@0: #include michael@0: extern "C" char* PrintJSStack(); michael@0: static void LogFunctionAndJSStack(const char* funcname) { michael@0: char *jsstack = PrintJSStack(); michael@0: __android_log_print(ANDROID_LOG_INFO, "PowerManagerService", \ michael@0: "Call to %s. The JS stack is:\n%s\n", michael@0: funcname, michael@0: jsstack ? jsstack : ""); michael@0: } michael@0: // bug 839452 michael@0: #define LOG_FUNCTION_AND_JS_STACK() \ michael@0: LogFunctionAndJSStack(__PRETTY_FUNCTION__); michael@0: #else michael@0: #define LOG_FUNCTION_AND_JS_STACK() michael@0: #endif michael@0: michael@0: namespace mozilla { michael@0: namespace dom { michael@0: namespace power { michael@0: michael@0: using namespace hal; michael@0: michael@0: NS_IMPL_ISUPPORTS(PowerManagerService, nsIPowerManagerService) michael@0: michael@0: /* static */ StaticRefPtr PowerManagerService::sSingleton; michael@0: michael@0: /* static */ already_AddRefed michael@0: PowerManagerService::GetInstance() michael@0: { michael@0: if (!sSingleton) { michael@0: sSingleton = new PowerManagerService(); michael@0: sSingleton->Init(); michael@0: ClearOnShutdown(&sSingleton); michael@0: } michael@0: michael@0: nsRefPtr service = sSingleton.get(); michael@0: return service.forget(); michael@0: } michael@0: michael@0: void michael@0: PowerManagerService::Init() michael@0: { michael@0: RegisterWakeLockObserver(this); michael@0: michael@0: // NB: default to *enabling* the watchdog even when the pref is michael@0: // absent, in case the profile might be damaged and we need to michael@0: // restart to repair it. michael@0: mWatchdogTimeoutSecs = michael@0: Preferences::GetInt("shutdown.watchdog.timeoutSecs", 5); michael@0: } michael@0: michael@0: PowerManagerService::~PowerManagerService() michael@0: { michael@0: UnregisterWakeLockObserver(this); michael@0: } michael@0: michael@0: void michael@0: PowerManagerService::ComputeWakeLockState(const WakeLockInformation& aWakeLockInfo, michael@0: nsAString &aState) michael@0: { michael@0: WakeLockState state = hal::ComputeWakeLockState(aWakeLockInfo.numLocks(), michael@0: aWakeLockInfo.numHidden()); michael@0: switch (state) { michael@0: case WAKE_LOCK_STATE_UNLOCKED: michael@0: aState.AssignLiteral("unlocked"); michael@0: break; michael@0: case WAKE_LOCK_STATE_HIDDEN: michael@0: aState.AssignLiteral("locked-background"); michael@0: break; michael@0: case WAKE_LOCK_STATE_VISIBLE: michael@0: aState.AssignLiteral("locked-foreground"); michael@0: break; michael@0: } michael@0: } michael@0: michael@0: void michael@0: PowerManagerService::Notify(const WakeLockInformation& aWakeLockInfo) michael@0: { michael@0: nsAutoString state; michael@0: ComputeWakeLockState(aWakeLockInfo, state); michael@0: michael@0: /** michael@0: * Copy the listeners list before we walk through the callbacks michael@0: * because the callbacks may install new listeners. We expect no michael@0: * more than one listener per window, so it shouldn't be too long. michael@0: */ michael@0: nsAutoTArray, 2> listeners(mWakeLockListeners); michael@0: michael@0: for (uint32_t i = 0; i < listeners.Length(); ++i) { michael@0: listeners[i]->Callback(aWakeLockInfo.topic(), state); michael@0: } michael@0: } michael@0: michael@0: void michael@0: PowerManagerService::SyncProfile() michael@0: { michael@0: nsCOMPtr obsServ = services::GetObserverService(); michael@0: if (obsServ) { michael@0: NS_NAMED_LITERAL_STRING(context, "shutdown-persist"); michael@0: obsServ->NotifyObservers(nullptr, "profile-change-net-teardown", context.get()); michael@0: obsServ->NotifyObservers(nullptr, "profile-change-teardown", context.get()); michael@0: obsServ->NotifyObservers(nullptr, "profile-before-change", context.get()); michael@0: obsServ->NotifyObservers(nullptr, "profile-before-change2", context.get()); michael@0: } michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: PowerManagerService::Reboot() michael@0: { michael@0: LOG_FUNCTION_AND_JS_STACK() // bug 839452 michael@0: michael@0: StartForceQuitWatchdog(eHalShutdownMode_Reboot, mWatchdogTimeoutSecs); michael@0: // To synchronize any unsaved user data before rebooting. michael@0: SyncProfile(); michael@0: hal::Reboot(); michael@0: MOZ_CRASH("hal::Reboot() shouldn't return"); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: PowerManagerService::PowerOff() michael@0: { michael@0: LOG_FUNCTION_AND_JS_STACK() // bug 839452 michael@0: michael@0: StartForceQuitWatchdog(eHalShutdownMode_PowerOff, mWatchdogTimeoutSecs); michael@0: // To synchronize any unsaved user data before powering off. michael@0: SyncProfile(); michael@0: hal::PowerOff(); michael@0: MOZ_CRASH("hal::PowerOff() shouldn't return"); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: PowerManagerService::Restart() michael@0: { michael@0: LOG_FUNCTION_AND_JS_STACK() // bug 839452 michael@0: michael@0: // FIXME/bug 796826 this implementation is currently gonk-specific, michael@0: // because it relies on the Gonk to initialize the Gecko processes to michael@0: // restart B2G. It's better to do it here to have a real "restart". michael@0: StartForceQuitWatchdog(eHalShutdownMode_Restart, mWatchdogTimeoutSecs); michael@0: // Ensure all content processes are dead before we continue michael@0: // restarting. This code is used to restart to apply updates, and michael@0: // if we don't join all the subprocesses, race conditions can cause michael@0: // them to see an inconsistent view of the application directory. michael@0: ContentParent::JoinAllSubprocesses(); michael@0: michael@0: // To synchronize any unsaved user data before restarting. michael@0: SyncProfile(); michael@0: #ifdef XP_UNIX michael@0: sync(); michael@0: #endif michael@0: _exit(0); michael@0: MOZ_CRASH("_exit() shouldn't return"); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: PowerManagerService::AddWakeLockListener(nsIDOMMozWakeLockListener *aListener) michael@0: { michael@0: if (mWakeLockListeners.Contains(aListener)) michael@0: return NS_OK; michael@0: michael@0: mWakeLockListeners.AppendElement(aListener); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: PowerManagerService::RemoveWakeLockListener(nsIDOMMozWakeLockListener *aListener) michael@0: { michael@0: mWakeLockListeners.RemoveElement(aListener); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: PowerManagerService::GetWakeLockState(const nsAString &aTopic, nsAString &aState) michael@0: { michael@0: WakeLockInformation info; michael@0: GetWakeLockInfo(aTopic, &info); michael@0: michael@0: ComputeWakeLockState(info, aState); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: already_AddRefed michael@0: PowerManagerService::NewWakeLock(const nsAString& aTopic, michael@0: nsIDOMWindow* aWindow, michael@0: mozilla::ErrorResult& aRv) michael@0: { michael@0: nsRefPtr wakelock = new WakeLock(); michael@0: aRv = wakelock->Init(aTopic, aWindow); michael@0: if (aRv.Failed()) { michael@0: return nullptr; michael@0: } michael@0: michael@0: return wakelock.forget(); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: PowerManagerService::NewWakeLock(const nsAString &aTopic, michael@0: nsIDOMWindow *aWindow, michael@0: nsISupports **aWakeLock) michael@0: { michael@0: mozilla::ErrorResult rv; michael@0: nsRefPtr wakelock = NewWakeLock(aTopic, aWindow, rv); michael@0: if (rv.Failed()) { michael@0: return rv.ErrorCode(); michael@0: } michael@0: michael@0: nsCOMPtr eventListener = wakelock.get(); michael@0: eventListener.forget(aWakeLock); michael@0: return NS_OK; michael@0: } michael@0: michael@0: already_AddRefed michael@0: PowerManagerService::NewWakeLockOnBehalfOfProcess(const nsAString& aTopic, michael@0: ContentParent* aContentParent) michael@0: { michael@0: nsRefPtr wakelock = new WakeLock(); michael@0: nsresult rv = wakelock->Init(aTopic, aContentParent); michael@0: NS_ENSURE_SUCCESS(rv, nullptr); michael@0: return wakelock.forget(); michael@0: } michael@0: michael@0: } // power michael@0: } // dom michael@0: } // mozilla