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/. */
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
;
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;
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
50 WakeLockInformation info
;
51 info
.topic() = aTopic
;
52 info
.numLocks() = aLockCount
.numLocks
;
53 info
.numHidden() = aLockCount
.numHidden
;
54 info
.lockingProcesses().AppendElements(aLockCount
.processes
);
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() {}
82 NS_IMPL_ISUPPORTS(ClearHashtableOnShutdown
, nsIObserver
)
85 ClearHashtableOnShutdown::Observe(nsISupports
* aSubject
, const char* aTopic
, const char16_t
* data
)
87 MOZ_ASSERT(!strcmp(aTopic
, "xpcom-shutdown"));
89 sIsShuttingDown
= true;
95 class CleanupOnContentShutdown final
: public nsIObserver
{
96 ~CleanupOnContentShutdown() {}
102 NS_IMPL_ISUPPORTS(CleanupOnContentShutdown
, nsIObserver
)
105 CleanupOnContentShutdown::Observe(nsISupports
* aSubject
, const char* aTopic
, const char16_t
* data
)
107 MOZ_ASSERT(!strcmp(aTopic
, "ipc:content-shutdown"));
109 if (sIsShuttingDown
) {
113 nsCOMPtr
<nsIPropertyBag2
> props
= do_QueryInterface(aSubject
);
115 NS_WARNING("ipc:content-shutdown message without property bag as subject");
119 uint64_t childID
= 0;
120 nsresult rv
= props
->GetPropertyAsUint64(NS_LITERAL_STRING("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(),
137 if (totalCount
.numLocks
== 0) {
143 NS_WARNING("ipc:content-shutdown message without childID property");
151 sLockTable
= new LockTable();
154 nsCOMPtr
<nsIObserverService
> obs
= mozilla::services::GetObserverService();
156 obs
->AddObserver(new ClearHashtableOnShutdown(), "xpcom-shutdown", false);
157 obs
->AddObserver(new CleanupOnContentShutdown(), "ipc:content-shutdown", false);
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
;
175 return WAKE_LOCK_STATE_VISIBLE
;
184 EnableWakeLockNotifications()
190 DisableWakeLockNotifications()
196 ModifyWakeLock(const nsAString
& aTopic
,
197 hal::WakeLockControl aLockAdjust
,
198 hal::WakeLockControl aHiddenAdjust
,
201 MOZ_ASSERT(NS_IsMainThread());
202 MOZ_ASSERT(aProcessID
!= CONTENT_PROCESS_ID_UNKNOWN
);
204 if (sIsShuttingDown
) {
211 ProcessLockTable
* table
= sLockTable
->Get(aTopic
);
212 LockCount processCount
;
213 LockCount totalCount
;
215 table
= new ProcessLockTable();
216 sLockTable
->Put(aTopic
, table
);
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
);
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
);
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();
270 ProcessLockTable
* table
= sLockTable
->Get(aTopic
);
272 *aWakeLockInfo
= WakeLockInfoFromLockCount(aTopic
, LockCount());
275 LockCount totalCount
;
276 CountWakeLocks(table
, &totalCount
);
277 *aWakeLockInfo
= WakeLockInfoFromLockCount(aTopic
, totalCount
);
280 } // namespace hal_impl
281 } // namespace mozilla