Line data Source code
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/. */
6 :
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 "nsAutoPtr.h"
13 : #include "nsClassHashtable.h"
14 : #include "nsDataHashtable.h"
15 : #include "nsHashKeys.h"
16 : #include "nsIPropertyBag2.h"
17 : #include "nsIObserverService.h"
18 :
19 : using namespace mozilla;
20 : using namespace mozilla::hal;
21 :
22 : namespace {
23 :
24 0 : struct LockCount {
25 0 : LockCount()
26 0 : : numLocks(0)
27 0 : , numHidden(0)
28 0 : {}
29 : uint32_t numLocks;
30 : uint32_t numHidden;
31 : nsTArray<uint64_t> processes;
32 : };
33 :
34 : typedef nsDataHashtable<nsUint64HashKey, LockCount> ProcessLockTable;
35 : typedef nsClassHashtable<nsStringHashKey, ProcessLockTable> LockTable;
36 :
37 : int sActiveListeners = 0;
38 3 : StaticAutoPtr<LockTable> sLockTable;
39 : bool sInitialized = false;
40 : bool sIsShuttingDown = false;
41 :
42 : WakeLockInformation
43 0 : WakeLockInfoFromLockCount(const nsAString& aTopic, const LockCount& aLockCount)
44 : {
45 : // TODO: Once we abandon b2g18, we can switch this to use the
46 : // WakeLockInformation constructor, which is better because it doesn't let us
47 : // forget to assign a param. For now we have to do it this way, because
48 : // b2g18 doesn't have the nsTArray <--> InfallibleTArray conversion (bug
49 : // 819791).
50 :
51 0 : WakeLockInformation info;
52 0 : info.topic() = aTopic;
53 0 : info.numLocks() = aLockCount.numLocks;
54 0 : info.numHidden() = aLockCount.numHidden;
55 0 : info.lockingProcesses().AppendElements(aLockCount.processes);
56 0 : return info;
57 : }
58 :
59 : static void
60 0 : CountWakeLocks(ProcessLockTable* aTable, LockCount* aTotalCount)
61 : {
62 0 : for (auto iter = aTable->Iter(); !iter.Done(); iter.Next()) {
63 0 : const uint64_t& key = iter.Key();
64 0 : LockCount count = iter.UserData();
65 :
66 0 : aTotalCount->numLocks += count.numLocks;
67 0 : aTotalCount->numHidden += count.numHidden;
68 :
69 : // This is linear in the number of processes, but that should be small.
70 0 : if (!aTotalCount->processes.Contains(key)) {
71 0 : aTotalCount->processes.AppendElement(key);
72 : }
73 : }
74 0 : }
75 :
76 0 : class ClearHashtableOnShutdown final : public nsIObserver {
77 0 : ~ClearHashtableOnShutdown() {}
78 : public:
79 : NS_DECL_ISUPPORTS
80 : NS_DECL_NSIOBSERVER
81 : };
82 :
83 0 : NS_IMPL_ISUPPORTS(ClearHashtableOnShutdown, nsIObserver)
84 :
85 : NS_IMETHODIMP
86 0 : ClearHashtableOnShutdown::Observe(nsISupports* aSubject, const char* aTopic, const char16_t* data)
87 : {
88 0 : MOZ_ASSERT(!strcmp(aTopic, "xpcom-shutdown"));
89 :
90 0 : sIsShuttingDown = true;
91 0 : sLockTable = nullptr;
92 :
93 0 : return NS_OK;
94 : }
95 :
96 0 : class CleanupOnContentShutdown final : public nsIObserver {
97 0 : ~CleanupOnContentShutdown() {}
98 : public:
99 : NS_DECL_ISUPPORTS
100 : NS_DECL_NSIOBSERVER
101 : };
102 :
103 0 : NS_IMPL_ISUPPORTS(CleanupOnContentShutdown, nsIObserver)
104 :
105 : NS_IMETHODIMP
106 0 : CleanupOnContentShutdown::Observe(nsISupports* aSubject, const char* aTopic, const char16_t* data)
107 : {
108 0 : MOZ_ASSERT(!strcmp(aTopic, "ipc:content-shutdown"));
109 :
110 0 : if (sIsShuttingDown) {
111 0 : return NS_OK;
112 : }
113 :
114 0 : nsCOMPtr<nsIPropertyBag2> props = do_QueryInterface(aSubject);
115 0 : if (!props) {
116 0 : NS_WARNING("ipc:content-shutdown message without property bag as subject");
117 0 : return NS_OK;
118 : }
119 :
120 0 : uint64_t childID = 0;
121 0 : nsresult rv = props->GetPropertyAsUint64(NS_LITERAL_STRING("childID"),
122 0 : &childID);
123 0 : if (NS_SUCCEEDED(rv)) {
124 0 : for (auto iter = sLockTable->Iter(); !iter.Done(); iter.Next()) {
125 0 : nsAutoPtr<ProcessLockTable>& table = iter.Data();
126 :
127 0 : if (table->Get(childID, nullptr)) {
128 0 : table->Remove(childID);
129 :
130 0 : LockCount totalCount;
131 0 : CountWakeLocks(table, &totalCount);
132 :
133 0 : if (sActiveListeners) {
134 0 : NotifyWakeLockChange(WakeLockInfoFromLockCount(iter.Key(),
135 0 : totalCount));
136 : }
137 :
138 0 : if (totalCount.numLocks == 0) {
139 0 : iter.Remove();
140 : }
141 : }
142 : }
143 : } else {
144 0 : NS_WARNING("ipc:content-shutdown message without childID property");
145 : }
146 0 : return NS_OK;
147 : }
148 :
149 : void
150 0 : Init()
151 : {
152 0 : sLockTable = new LockTable();
153 0 : sInitialized = true;
154 :
155 0 : nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
156 0 : if (obs) {
157 0 : obs->AddObserver(new ClearHashtableOnShutdown(), "xpcom-shutdown", false);
158 0 : obs->AddObserver(new CleanupOnContentShutdown(), "ipc:content-shutdown", false);
159 : }
160 0 : }
161 :
162 : } // namespace
163 :
164 : namespace mozilla {
165 :
166 : namespace hal {
167 :
168 : WakeLockState
169 0 : ComputeWakeLockState(int aNumLocks, int aNumHidden)
170 : {
171 0 : if (aNumLocks == 0) {
172 0 : return WAKE_LOCK_STATE_UNLOCKED;
173 0 : } else if (aNumLocks == aNumHidden) {
174 0 : return WAKE_LOCK_STATE_HIDDEN;
175 : } else {
176 0 : return WAKE_LOCK_STATE_VISIBLE;
177 : }
178 : }
179 :
180 : } // namespace hal
181 :
182 : namespace hal_impl {
183 :
184 : void
185 1 : EnableWakeLockNotifications()
186 : {
187 1 : sActiveListeners++;
188 1 : }
189 :
190 : void
191 0 : DisableWakeLockNotifications()
192 : {
193 0 : sActiveListeners--;
194 0 : }
195 :
196 : void
197 0 : ModifyWakeLock(const nsAString& aTopic,
198 : hal::WakeLockControl aLockAdjust,
199 : hal::WakeLockControl aHiddenAdjust,
200 : uint64_t aProcessID)
201 : {
202 0 : MOZ_ASSERT(NS_IsMainThread());
203 0 : MOZ_ASSERT(aProcessID != CONTENT_PROCESS_ID_UNKNOWN);
204 :
205 0 : if (sIsShuttingDown) {
206 0 : return;
207 : }
208 0 : if (!sInitialized) {
209 0 : Init();
210 : }
211 :
212 0 : ProcessLockTable* table = sLockTable->Get(aTopic);
213 0 : LockCount processCount;
214 0 : LockCount totalCount;
215 0 : if (!table) {
216 0 : table = new ProcessLockTable();
217 0 : sLockTable->Put(aTopic, table);
218 : } else {
219 0 : table->Get(aProcessID, &processCount);
220 0 : CountWakeLocks(table, &totalCount);
221 : }
222 :
223 0 : MOZ_ASSERT(processCount.numLocks >= processCount.numHidden);
224 0 : MOZ_ASSERT(aLockAdjust >= 0 || processCount.numLocks > 0);
225 0 : MOZ_ASSERT(aHiddenAdjust >= 0 || processCount.numHidden > 0);
226 0 : MOZ_ASSERT(totalCount.numLocks >= totalCount.numHidden);
227 0 : MOZ_ASSERT(aLockAdjust >= 0 || totalCount.numLocks > 0);
228 0 : MOZ_ASSERT(aHiddenAdjust >= 0 || totalCount.numHidden > 0);
229 :
230 0 : WakeLockState oldState = ComputeWakeLockState(totalCount.numLocks, totalCount.numHidden);
231 0 : bool processWasLocked = processCount.numLocks > 0;
232 :
233 0 : processCount.numLocks += aLockAdjust;
234 0 : processCount.numHidden += aHiddenAdjust;
235 :
236 0 : totalCount.numLocks += aLockAdjust;
237 0 : totalCount.numHidden += aHiddenAdjust;
238 :
239 0 : if (processCount.numLocks) {
240 0 : table->Put(aProcessID, processCount);
241 : } else {
242 0 : table->Remove(aProcessID);
243 : }
244 0 : if (!totalCount.numLocks) {
245 0 : sLockTable->Remove(aTopic);
246 : }
247 :
248 0 : if (sActiveListeners &&
249 0 : (oldState != ComputeWakeLockState(totalCount.numLocks,
250 0 : totalCount.numHidden) ||
251 0 : processWasLocked != (processCount.numLocks > 0))) {
252 :
253 0 : WakeLockInformation info;
254 0 : hal::GetWakeLockInfo(aTopic, &info);
255 0 : NotifyWakeLockChange(info);
256 : }
257 : }
258 :
259 : void
260 0 : GetWakeLockInfo(const nsAString& aTopic, WakeLockInformation* aWakeLockInfo)
261 : {
262 0 : if (sIsShuttingDown) {
263 0 : NS_WARNING("You don't want to get wake lock information during xpcom-shutdown!");
264 0 : *aWakeLockInfo = WakeLockInformation();
265 0 : return;
266 : }
267 0 : if (!sInitialized) {
268 0 : Init();
269 : }
270 :
271 0 : ProcessLockTable* table = sLockTable->Get(aTopic);
272 0 : if (!table) {
273 0 : *aWakeLockInfo = WakeLockInfoFromLockCount(aTopic, LockCount());
274 0 : return;
275 : }
276 0 : LockCount totalCount;
277 0 : CountWakeLocks(table, &totalCount);
278 0 : *aWakeLockInfo = WakeLockInfoFromLockCount(aTopic, totalCount);
279 : }
280 :
281 : } // namespace hal_impl
282 : } // namespace mozilla
|