Bumping manifests a=b2g-bump
[gecko.git] / hal / HalWakeLock.cpp
blob0c1eee47a89e7c3a149ca33b897e42244fb14178
1 /* -*- Mode: C++; tab-width: 40; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
4 * You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "mozilla/Hal.h"
7 #include "mozilla/HalWakeLock.h"
8 #include "mozilla/Services.h"
9 #include "mozilla/StaticPtr.h"
10 #include "mozilla/dom/ContentParent.h"
11 #include "nsClassHashtable.h"
12 #include "nsDataHashtable.h"
13 #include "nsHashKeys.h"
14 #include "nsIPropertyBag2.h"
15 #include "nsIObserverService.h"
17 using namespace mozilla;
18 using namespace mozilla::hal;
20 namespace {
22 struct LockCount {
23 LockCount()
24 : numLocks(0)
25 , numHidden(0)
27 uint32_t numLocks;
28 uint32_t numHidden;
29 nsTArray<uint64_t> processes;
32 typedef nsDataHashtable<nsUint64HashKey, LockCount> ProcessLockTable;
33 typedef nsClassHashtable<nsStringHashKey, ProcessLockTable> LockTable;
35 int sActiveListeners = 0;
36 StaticAutoPtr<LockTable> sLockTable;
37 bool sInitialized = false;
38 bool sIsShuttingDown = false;
40 WakeLockInformation
41 WakeLockInfoFromLockCount(const nsAString& aTopic, const LockCount& aLockCount)
43 // TODO: Once we abandon b2g18, we can switch this to use the
44 // WakeLockInformation constructor, which is better because it doesn't let us
45 // forget to assign a param. For now we have to do it this way, because
46 // b2g18 doesn't have the nsTArray <--> InfallibleTArray conversion (bug
47 // 819791).
49 WakeLockInformation info;
50 info.topic() = aTopic;
51 info.numLocks() = aLockCount.numLocks;
52 info.numHidden() = aLockCount.numHidden;
53 info.lockingProcesses().AppendElements(aLockCount.processes);
54 return info;
57 PLDHashOperator
58 CountWakeLocks(const uint64_t& aKey, LockCount aCount, void* aUserArg)
60 MOZ_ASSERT(aUserArg);
62 LockCount* totalCount = static_cast<LockCount*>(aUserArg);
63 totalCount->numLocks += aCount.numLocks;
64 totalCount->numHidden += aCount.numHidden;
66 // This is linear in the number of processes, but that should be small.
67 if (!totalCount->processes.Contains(aKey)) {
68 totalCount->processes.AppendElement(aKey);
71 return PL_DHASH_NEXT;
74 static PLDHashOperator
75 RemoveChildFromList(const nsAString& aKey, nsAutoPtr<ProcessLockTable>& aTable,
76 void* aUserArg)
78 MOZ_ASSERT(aUserArg);
80 PLDHashOperator op = PL_DHASH_NEXT;
81 uint64_t childID = *static_cast<uint64_t*>(aUserArg);
82 if (aTable->Get(childID, nullptr)) {
83 aTable->Remove(childID);
85 LockCount totalCount;
86 aTable->EnumerateRead(CountWakeLocks, &totalCount);
87 if (!totalCount.numLocks) {
88 op = PL_DHASH_REMOVE;
91 if (sActiveListeners) {
92 NotifyWakeLockChange(WakeLockInfoFromLockCount(aKey, totalCount));
96 return op;
99 class ClearHashtableOnShutdown MOZ_FINAL : public nsIObserver {
100 ~ClearHashtableOnShutdown() {}
101 public:
102 NS_DECL_ISUPPORTS
103 NS_DECL_NSIOBSERVER
106 NS_IMPL_ISUPPORTS(ClearHashtableOnShutdown, nsIObserver)
108 NS_IMETHODIMP
109 ClearHashtableOnShutdown::Observe(nsISupports* aSubject, const char* aTopic, const char16_t* data)
111 MOZ_ASSERT(!strcmp(aTopic, "xpcom-shutdown"));
113 sIsShuttingDown = true;
114 sLockTable = nullptr;
116 return NS_OK;
119 class CleanupOnContentShutdown MOZ_FINAL : public nsIObserver {
120 ~CleanupOnContentShutdown() {}
121 public:
122 NS_DECL_ISUPPORTS
123 NS_DECL_NSIOBSERVER
126 NS_IMPL_ISUPPORTS(CleanupOnContentShutdown, nsIObserver)
128 NS_IMETHODIMP
129 CleanupOnContentShutdown::Observe(nsISupports* aSubject, const char* aTopic, const char16_t* data)
131 MOZ_ASSERT(!strcmp(aTopic, "ipc:content-shutdown"));
133 if (sIsShuttingDown) {
134 return NS_OK;
137 nsCOMPtr<nsIPropertyBag2> props = do_QueryInterface(aSubject);
138 if (!props) {
139 NS_WARNING("ipc:content-shutdown message without property bag as subject");
140 return NS_OK;
143 uint64_t childID = 0;
144 nsresult rv = props->GetPropertyAsUint64(NS_LITERAL_STRING("childID"),
145 &childID);
146 if (NS_SUCCEEDED(rv)) {
147 sLockTable->Enumerate(RemoveChildFromList, &childID);
148 } else {
149 NS_WARNING("ipc:content-shutdown message without childID property");
151 return NS_OK;
154 void
155 Init()
157 sLockTable = new LockTable();
158 sInitialized = true;
160 nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
161 if (obs) {
162 obs->AddObserver(new ClearHashtableOnShutdown(), "xpcom-shutdown", false);
163 obs->AddObserver(new CleanupOnContentShutdown(), "ipc:content-shutdown", false);
167 } // anonymous namespace
169 namespace mozilla {
171 namespace hal {
173 WakeLockState
174 ComputeWakeLockState(int aNumLocks, int aNumHidden)
176 if (aNumLocks == 0) {
177 return WAKE_LOCK_STATE_UNLOCKED;
178 } else if (aNumLocks == aNumHidden) {
179 return WAKE_LOCK_STATE_HIDDEN;
180 } else {
181 return WAKE_LOCK_STATE_VISIBLE;
185 } // namespace hal
187 namespace hal_impl {
189 void
190 EnableWakeLockNotifications()
192 sActiveListeners++;
195 void
196 DisableWakeLockNotifications()
198 sActiveListeners--;
201 void
202 ModifyWakeLock(const nsAString& aTopic,
203 hal::WakeLockControl aLockAdjust,
204 hal::WakeLockControl aHiddenAdjust,
205 uint64_t aProcessID)
207 MOZ_ASSERT(NS_IsMainThread());
208 MOZ_ASSERT(aProcessID != CONTENT_PROCESS_ID_UNKNOWN);
210 if (sIsShuttingDown) {
211 return;
213 if (!sInitialized) {
214 Init();
217 ProcessLockTable* table = sLockTable->Get(aTopic);
218 LockCount processCount;
219 LockCount totalCount;
220 if (!table) {
221 table = new ProcessLockTable();
222 sLockTable->Put(aTopic, table);
223 } else {
224 table->Get(aProcessID, &processCount);
225 table->EnumerateRead(CountWakeLocks, &totalCount);
228 MOZ_ASSERT(processCount.numLocks >= processCount.numHidden);
229 MOZ_ASSERT(aLockAdjust >= 0 || processCount.numLocks > 0);
230 MOZ_ASSERT(aHiddenAdjust >= 0 || processCount.numHidden > 0);
231 MOZ_ASSERT(totalCount.numLocks >= totalCount.numHidden);
232 MOZ_ASSERT(aLockAdjust >= 0 || totalCount.numLocks > 0);
233 MOZ_ASSERT(aHiddenAdjust >= 0 || totalCount.numHidden > 0);
235 WakeLockState oldState = ComputeWakeLockState(totalCount.numLocks, totalCount.numHidden);
236 bool processWasLocked = processCount.numLocks > 0;
238 processCount.numLocks += aLockAdjust;
239 processCount.numHidden += aHiddenAdjust;
241 totalCount.numLocks += aLockAdjust;
242 totalCount.numHidden += aHiddenAdjust;
244 if (processCount.numLocks) {
245 table->Put(aProcessID, processCount);
246 } else {
247 table->Remove(aProcessID);
249 if (!totalCount.numLocks) {
250 sLockTable->Remove(aTopic);
253 if (sActiveListeners &&
254 (oldState != ComputeWakeLockState(totalCount.numLocks,
255 totalCount.numHidden) ||
256 processWasLocked != (processCount.numLocks > 0))) {
258 WakeLockInformation info;
259 hal::GetWakeLockInfo(aTopic, &info);
260 NotifyWakeLockChange(info);
264 void
265 GetWakeLockInfo(const nsAString& aTopic, WakeLockInformation* aWakeLockInfo)
267 if (sIsShuttingDown) {
268 NS_WARNING("You don't want to get wake lock information during xpcom-shutdown!");
269 *aWakeLockInfo = WakeLockInformation();
270 return;
272 if (!sInitialized) {
273 Init();
276 ProcessLockTable* table = sLockTable->Get(aTopic);
277 if (!table) {
278 *aWakeLockInfo = WakeLockInfoFromLockCount(aTopic, LockCount());
279 return;
281 LockCount totalCount;
282 table->EnumerateRead(CountWakeLocks, &totalCount);
283 *aWakeLockInfo = WakeLockInfoFromLockCount(aTopic, totalCount);
286 } // hal_impl
287 } // mozilla