summaryrefslogtreecommitdiffstats
path: root/hal/HalWakeLock.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'hal/HalWakeLock.cpp')
-rw-r--r--hal/HalWakeLock.cpp282
1 files changed, 282 insertions, 0 deletions
diff --git a/hal/HalWakeLock.cpp b/hal/HalWakeLock.cpp
new file mode 100644
index 000000000..af4dc454e
--- /dev/null
+++ b/hal/HalWakeLock.cpp
@@ -0,0 +1,282 @@
+/* -*- 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 "Hal.h"
+#include "mozilla/HalWakeLock.h"
+#include "mozilla/Services.h"
+#include "mozilla/StaticPtr.h"
+#include "mozilla/dom/ContentParent.h"
+#include "nsAutoPtr.h"
+#include "nsClassHashtable.h"
+#include "nsDataHashtable.h"
+#include "nsHashKeys.h"
+#include "nsIPropertyBag2.h"
+#include "nsIObserverService.h"
+
+using namespace mozilla;
+using namespace mozilla::hal;
+
+namespace {
+
+struct LockCount {
+ LockCount()
+ : numLocks(0)
+ , numHidden(0)
+ {}
+ uint32_t numLocks;
+ uint32_t numHidden;
+ nsTArray<uint64_t> processes;
+};
+
+typedef nsDataHashtable<nsUint64HashKey, LockCount> ProcessLockTable;
+typedef nsClassHashtable<nsStringHashKey, ProcessLockTable> LockTable;
+
+int sActiveListeners = 0;
+StaticAutoPtr<LockTable> sLockTable;
+bool sInitialized = false;
+bool sIsShuttingDown = false;
+
+WakeLockInformation
+WakeLockInfoFromLockCount(const nsAString& aTopic, const LockCount& aLockCount)
+{
+ // TODO: Once we abandon b2g18, we can switch this to use the
+ // WakeLockInformation constructor, which is better because it doesn't let us
+ // forget to assign a param. For now we have to do it this way, because
+ // b2g18 doesn't have the nsTArray <--> InfallibleTArray conversion (bug
+ // 819791).
+
+ WakeLockInformation info;
+ info.topic() = aTopic;
+ info.numLocks() = aLockCount.numLocks;
+ info.numHidden() = aLockCount.numHidden;
+ info.lockingProcesses().AppendElements(aLockCount.processes);
+ return info;
+}
+
+static void
+CountWakeLocks(ProcessLockTable* aTable, LockCount* aTotalCount)
+{
+ for (auto iter = aTable->Iter(); !iter.Done(); iter.Next()) {
+ const uint64_t& key = iter.Key();
+ LockCount count = iter.UserData();
+
+ aTotalCount->numLocks += count.numLocks;
+ aTotalCount->numHidden += count.numHidden;
+
+ // This is linear in the number of processes, but that should be small.
+ if (!aTotalCount->processes.Contains(key)) {
+ aTotalCount->processes.AppendElement(key);
+ }
+ }
+}
+
+class ClearHashtableOnShutdown final : public nsIObserver {
+ ~ClearHashtableOnShutdown() {}
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIOBSERVER
+};
+
+NS_IMPL_ISUPPORTS(ClearHashtableOnShutdown, nsIObserver)
+
+NS_IMETHODIMP
+ClearHashtableOnShutdown::Observe(nsISupports* aSubject, const char* aTopic, const char16_t* data)
+{
+ MOZ_ASSERT(!strcmp(aTopic, "xpcom-shutdown"));
+
+ sIsShuttingDown = true;
+ sLockTable = nullptr;
+
+ return NS_OK;
+}
+
+class CleanupOnContentShutdown final : public nsIObserver {
+ ~CleanupOnContentShutdown() {}
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIOBSERVER
+};
+
+NS_IMPL_ISUPPORTS(CleanupOnContentShutdown, nsIObserver)
+
+NS_IMETHODIMP
+CleanupOnContentShutdown::Observe(nsISupports* aSubject, const char* aTopic, const char16_t* data)
+{
+ MOZ_ASSERT(!strcmp(aTopic, "ipc:content-shutdown"));
+
+ if (sIsShuttingDown) {
+ return NS_OK;
+ }
+
+ nsCOMPtr<nsIPropertyBag2> props = do_QueryInterface(aSubject);
+ if (!props) {
+ NS_WARNING("ipc:content-shutdown message without property bag as subject");
+ return NS_OK;
+ }
+
+ uint64_t childID = 0;
+ nsresult rv = props->GetPropertyAsUint64(NS_LITERAL_STRING("childID"),
+ &childID);
+ if (NS_SUCCEEDED(rv)) {
+ for (auto iter = sLockTable->Iter(); !iter.Done(); iter.Next()) {
+ nsAutoPtr<ProcessLockTable>& table = iter.Data();
+
+ if (table->Get(childID, nullptr)) {
+ table->Remove(childID);
+
+ LockCount totalCount;
+ CountWakeLocks(table, &totalCount);
+
+ if (sActiveListeners) {
+ NotifyWakeLockChange(WakeLockInfoFromLockCount(iter.Key(),
+ totalCount));
+ }
+
+ if (totalCount.numLocks == 0) {
+ iter.Remove();
+ }
+ }
+ }
+ } else {
+ NS_WARNING("ipc:content-shutdown message without childID property");
+ }
+ return NS_OK;
+}
+
+void
+Init()
+{
+ sLockTable = new LockTable();
+ sInitialized = true;
+
+ nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
+ if (obs) {
+ obs->AddObserver(new ClearHashtableOnShutdown(), "xpcom-shutdown", false);
+ obs->AddObserver(new CleanupOnContentShutdown(), "ipc:content-shutdown", false);
+ }
+}
+
+} // namespace
+
+namespace mozilla {
+
+namespace hal {
+
+WakeLockState
+ComputeWakeLockState(int aNumLocks, int aNumHidden)
+{
+ if (aNumLocks == 0) {
+ return WAKE_LOCK_STATE_UNLOCKED;
+ } else if (aNumLocks == aNumHidden) {
+ return WAKE_LOCK_STATE_HIDDEN;
+ } else {
+ return WAKE_LOCK_STATE_VISIBLE;
+ }
+}
+
+} // namespace hal
+
+namespace hal_impl {
+
+void
+EnableWakeLockNotifications()
+{
+ sActiveListeners++;
+}
+
+void
+DisableWakeLockNotifications()
+{
+ sActiveListeners--;
+}
+
+void
+ModifyWakeLock(const nsAString& aTopic,
+ hal::WakeLockControl aLockAdjust,
+ hal::WakeLockControl aHiddenAdjust,
+ uint64_t aProcessID)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(aProcessID != CONTENT_PROCESS_ID_UNKNOWN);
+
+ if (sIsShuttingDown) {
+ return;
+ }
+ if (!sInitialized) {
+ Init();
+ }
+
+ ProcessLockTable* table = sLockTable->Get(aTopic);
+ LockCount processCount;
+ LockCount totalCount;
+ if (!table) {
+ table = new ProcessLockTable();
+ sLockTable->Put(aTopic, table);
+ } else {
+ table->Get(aProcessID, &processCount);
+ CountWakeLocks(table, &totalCount);
+ }
+
+ MOZ_ASSERT(processCount.numLocks >= processCount.numHidden);
+ MOZ_ASSERT(aLockAdjust >= 0 || processCount.numLocks > 0);
+ MOZ_ASSERT(aHiddenAdjust >= 0 || processCount.numHidden > 0);
+ MOZ_ASSERT(totalCount.numLocks >= totalCount.numHidden);
+ MOZ_ASSERT(aLockAdjust >= 0 || totalCount.numLocks > 0);
+ MOZ_ASSERT(aHiddenAdjust >= 0 || totalCount.numHidden > 0);
+
+ WakeLockState oldState = ComputeWakeLockState(totalCount.numLocks, totalCount.numHidden);
+ bool processWasLocked = processCount.numLocks > 0;
+
+ processCount.numLocks += aLockAdjust;
+ processCount.numHidden += aHiddenAdjust;
+
+ totalCount.numLocks += aLockAdjust;
+ totalCount.numHidden += aHiddenAdjust;
+
+ if (processCount.numLocks) {
+ table->Put(aProcessID, processCount);
+ } else {
+ table->Remove(aProcessID);
+ }
+ if (!totalCount.numLocks) {
+ sLockTable->Remove(aTopic);
+ }
+
+ if (sActiveListeners &&
+ (oldState != ComputeWakeLockState(totalCount.numLocks,
+ totalCount.numHidden) ||
+ processWasLocked != (processCount.numLocks > 0))) {
+
+ WakeLockInformation info;
+ hal::GetWakeLockInfo(aTopic, &info);
+ NotifyWakeLockChange(info);
+ }
+}
+
+void
+GetWakeLockInfo(const nsAString& aTopic, WakeLockInformation* aWakeLockInfo)
+{
+ if (sIsShuttingDown) {
+ NS_WARNING("You don't want to get wake lock information during xpcom-shutdown!");
+ *aWakeLockInfo = WakeLockInformation();
+ return;
+ }
+ if (!sInitialized) {
+ Init();
+ }
+
+ ProcessLockTable* table = sLockTable->Get(aTopic);
+ if (!table) {
+ *aWakeLockInfo = WakeLockInfoFromLockCount(aTopic, LockCount());
+ return;
+ }
+ LockCount totalCount;
+ CountWakeLocks(table, &totalCount);
+ *aWakeLockInfo = WakeLockInfoFromLockCount(aTopic, totalCount);
+}
+
+} // namespace hal_impl
+} // namespace mozilla