Backed out changeset 2284c3e8c336 (bug 1215092)
[gecko.git] / hal / HalWakeLock.cpp
bloba4838bcf3670153e1ac5278729bd47b805f2c7e1
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
5 * You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "Hal.h"
8 #include "mozilla/HalWakeLock.h"
9 #include "mozilla/Services.h"
10 #include "mozilla/StaticPtr.h"
11 #include "mozilla/dom/ContentParent.h"
12 #include "nsClassHashtable.h"
13 #include "nsDataHashtable.h"
14 #include "nsHashKeys.h"
15 #include "nsIPropertyBag2.h"
16 #include "nsIObserverService.h"
18 using namespace mozilla;
19 using namespace mozilla::hal;
21 namespace {
23 struct LockCount {
24 LockCount()
25 : numLocks(0)
26 , numHidden(0)
28 uint32_t numLocks;
29 uint32_t numHidden;
30 nsTArray<uint64_t> processes;
33 typedef nsDataHashtable<nsUint64HashKey, LockCount> ProcessLockTable;
34 typedef nsClassHashtable<nsStringHashKey, ProcessLockTable> LockTable;
36 int sActiveListeners = 0;
37 StaticAutoPtr<LockTable> sLockTable;
38 bool sInitialized = false;
39 bool sIsShuttingDown = false;
41 WakeLockInformation
42 WakeLockInfoFromLockCount(const nsAString& aTopic, const LockCount& aLockCount)
44 // TODO: Once we abandon b2g18, we can switch this to use the
45 // WakeLockInformation constructor, which is better because it doesn't let us
46 // forget to assign a param. For now we have to do it this way, because
47 // b2g18 doesn't have the nsTArray <--> InfallibleTArray conversion (bug
48 // 819791).
50 WakeLockInformation info;
51 info.topic() = aTopic;
52 info.numLocks() = aLockCount.numLocks;
53 info.numHidden() = aLockCount.numHidden;
54 info.lockingProcesses().AppendElements(aLockCount.processes);
55 return info;
58 static void
59 CountWakeLocks(ProcessLockTable* aTable, LockCount* aTotalCount)
61 for (auto iter = aTable->Iter(); !iter.Done(); iter.Next()) {
62 const uint64_t& key = iter.Key();
63 LockCount count = iter.UserData();
65 aTotalCount->numLocks += count.numLocks;
66 aTotalCount->numHidden += count.numHidden;
68 // This is linear in the number of processes, but that should be small.
69 if (!aTotalCount->processes.Contains(key)) {
70 aTotalCount->processes.AppendElement(key);
75 class ClearHashtableOnShutdown final : public nsIObserver {
76 ~ClearHashtableOnShutdown() {}
77 public:
78 NS_DECL_ISUPPORTS
79 NS_DECL_NSIOBSERVER
82 NS_IMPL_ISUPPORTS(ClearHashtableOnShutdown, nsIObserver)
84 NS_IMETHODIMP
85 ClearHashtableOnShutdown::Observe(nsISupports* aSubject, const char* aTopic, const char16_t* data)
87 MOZ_ASSERT(!strcmp(aTopic, "xpcom-shutdown"));
89 sIsShuttingDown = true;
90 sLockTable = nullptr;
92 return NS_OK;
95 class CleanupOnContentShutdown final : public nsIObserver {
96 ~CleanupOnContentShutdown() {}
97 public:
98 NS_DECL_ISUPPORTS
99 NS_DECL_NSIOBSERVER
102 NS_IMPL_ISUPPORTS(CleanupOnContentShutdown, nsIObserver)
104 NS_IMETHODIMP
105 CleanupOnContentShutdown::Observe(nsISupports* aSubject, const char* aTopic, const char16_t* data)
107 MOZ_ASSERT(!strcmp(aTopic, "ipc:content-shutdown"));
109 if (sIsShuttingDown) {
110 return NS_OK;
113 nsCOMPtr<nsIPropertyBag2> props = do_QueryInterface(aSubject);
114 if (!props) {
115 NS_WARNING("ipc:content-shutdown message without property bag as subject");
116 return NS_OK;
119 uint64_t childID = 0;
120 nsresult rv = props->GetPropertyAsUint64(NS_LITERAL_STRING("childID"),
121 &childID);
122 if (NS_SUCCEEDED(rv)) {
123 for (auto iter = sLockTable->Iter(); !iter.Done(); iter.Next()) {
124 nsAutoPtr<ProcessLockTable>& table = iter.Data();
126 if (table->Get(childID, nullptr)) {
127 table->Remove(childID);
129 LockCount totalCount;
130 CountWakeLocks(table, &totalCount);
132 if (sActiveListeners) {
133 NotifyWakeLockChange(WakeLockInfoFromLockCount(iter.Key(),
134 totalCount));
137 if (totalCount.numLocks == 0) {
138 iter.Remove();
142 } else {
143 NS_WARNING("ipc:content-shutdown message without childID property");
145 return NS_OK;
148 void
149 Init()
151 sLockTable = new LockTable();
152 sInitialized = true;
154 nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
155 if (obs) {
156 obs->AddObserver(new ClearHashtableOnShutdown(), "xpcom-shutdown", false);
157 obs->AddObserver(new CleanupOnContentShutdown(), "ipc:content-shutdown", false);
161 } // namespace
163 namespace mozilla {
165 namespace hal {
167 WakeLockState
168 ComputeWakeLockState(int aNumLocks, int aNumHidden)
170 if (aNumLocks == 0) {
171 return WAKE_LOCK_STATE_UNLOCKED;
172 } else if (aNumLocks == aNumHidden) {
173 return WAKE_LOCK_STATE_HIDDEN;
174 } else {
175 return WAKE_LOCK_STATE_VISIBLE;
179 } // namespace hal
181 namespace hal_impl {
183 void
184 EnableWakeLockNotifications()
186 sActiveListeners++;
189 void
190 DisableWakeLockNotifications()
192 sActiveListeners--;
195 void
196 ModifyWakeLock(const nsAString& aTopic,
197 hal::WakeLockControl aLockAdjust,
198 hal::WakeLockControl aHiddenAdjust,
199 uint64_t aProcessID)
201 MOZ_ASSERT(NS_IsMainThread());
202 MOZ_ASSERT(aProcessID != CONTENT_PROCESS_ID_UNKNOWN);
204 if (sIsShuttingDown) {
205 return;
207 if (!sInitialized) {
208 Init();
211 ProcessLockTable* table = sLockTable->Get(aTopic);
212 LockCount processCount;
213 LockCount totalCount;
214 if (!table) {
215 table = new ProcessLockTable();
216 sLockTable->Put(aTopic, table);
217 } else {
218 table->Get(aProcessID, &processCount);
219 CountWakeLocks(table, &totalCount);
222 MOZ_ASSERT(processCount.numLocks >= processCount.numHidden);
223 MOZ_ASSERT(aLockAdjust >= 0 || processCount.numLocks > 0);
224 MOZ_ASSERT(aHiddenAdjust >= 0 || processCount.numHidden > 0);
225 MOZ_ASSERT(totalCount.numLocks >= totalCount.numHidden);
226 MOZ_ASSERT(aLockAdjust >= 0 || totalCount.numLocks > 0);
227 MOZ_ASSERT(aHiddenAdjust >= 0 || totalCount.numHidden > 0);
229 WakeLockState oldState = ComputeWakeLockState(totalCount.numLocks, totalCount.numHidden);
230 bool processWasLocked = processCount.numLocks > 0;
232 processCount.numLocks += aLockAdjust;
233 processCount.numHidden += aHiddenAdjust;
235 totalCount.numLocks += aLockAdjust;
236 totalCount.numHidden += aHiddenAdjust;
238 if (processCount.numLocks) {
239 table->Put(aProcessID, processCount);
240 } else {
241 table->Remove(aProcessID);
243 if (!totalCount.numLocks) {
244 sLockTable->Remove(aTopic);
247 if (sActiveListeners &&
248 (oldState != ComputeWakeLockState(totalCount.numLocks,
249 totalCount.numHidden) ||
250 processWasLocked != (processCount.numLocks > 0))) {
252 WakeLockInformation info;
253 hal::GetWakeLockInfo(aTopic, &info);
254 NotifyWakeLockChange(info);
258 void
259 GetWakeLockInfo(const nsAString& aTopic, WakeLockInformation* aWakeLockInfo)
261 if (sIsShuttingDown) {
262 NS_WARNING("You don't want to get wake lock information during xpcom-shutdown!");
263 *aWakeLockInfo = WakeLockInformation();
264 return;
266 if (!sInitialized) {
267 Init();
270 ProcessLockTable* table = sLockTable->Get(aTopic);
271 if (!table) {
272 *aWakeLockInfo = WakeLockInfoFromLockCount(aTopic, LockCount());
273 return;
275 LockCount totalCount;
276 CountWakeLocks(table, &totalCount);
277 *aWakeLockInfo = WakeLockInfoFromLockCount(aTopic, totalCount);
280 } // namespace hal_impl
281 } // namespace mozilla