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 "nsAutoPtr.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
;
24 LockCount() : numLocks(0), numHidden(0) {}
27 nsTArray
<uint64_t> processes
;
30 typedef nsDataHashtable
<nsUint64HashKey
, LockCount
> ProcessLockTable
;
31 typedef nsClassHashtable
<nsStringHashKey
, ProcessLockTable
> LockTable
;
33 int sActiveListeners
= 0;
34 StaticAutoPtr
<LockTable
> sLockTable
;
35 bool sIsShuttingDown
= false;
37 WakeLockInformation
WakeLockInfoFromLockCount(const nsAString
& aTopic
,
38 const LockCount
& aLockCount
) {
39 nsString
topic(aTopic
);
40 WakeLockInformation
info(topic
, aLockCount
.numLocks
, aLockCount
.numHidden
,
41 aLockCount
.processes
);
46 static void CountWakeLocks(ProcessLockTable
* aTable
, LockCount
* aTotalCount
) {
47 for (auto iter
= aTable
->Iter(); !iter
.Done(); iter
.Next()) {
48 const uint64_t& key
= iter
.Key();
49 LockCount count
= iter
.UserData();
51 aTotalCount
->numLocks
+= count
.numLocks
;
52 aTotalCount
->numHidden
+= count
.numHidden
;
54 // This is linear in the number of processes, but that should be small.
55 if (!aTotalCount
->processes
.Contains(key
)) {
56 aTotalCount
->processes
.AppendElement(key
);
61 class ClearHashtableOnShutdown final
: public nsIObserver
{
62 ~ClearHashtableOnShutdown() {}
69 NS_IMPL_ISUPPORTS(ClearHashtableOnShutdown
, nsIObserver
)
72 ClearHashtableOnShutdown::Observe(nsISupports
* aSubject
, const char* aTopic
,
73 const char16_t
* data
) {
74 MOZ_ASSERT(!strcmp(aTopic
, "xpcom-shutdown"));
76 sIsShuttingDown
= true;
82 class CleanupOnContentShutdown final
: public nsIObserver
{
83 ~CleanupOnContentShutdown() {}
90 NS_IMPL_ISUPPORTS(CleanupOnContentShutdown
, nsIObserver
)
93 CleanupOnContentShutdown::Observe(nsISupports
* aSubject
, const char* aTopic
,
94 const char16_t
* data
) {
95 MOZ_ASSERT(!strcmp(aTopic
, "ipc:content-shutdown"));
97 if (sIsShuttingDown
) {
101 nsCOMPtr
<nsIPropertyBag2
> props
= do_QueryInterface(aSubject
);
103 NS_WARNING("ipc:content-shutdown message without property bag as subject");
107 uint64_t childID
= 0;
109 props
->GetPropertyAsUint64(NS_LITERAL_STRING("childID"), &childID
);
110 if (NS_SUCCEEDED(rv
)) {
111 for (auto iter
= sLockTable
->Iter(); !iter
.Done(); iter
.Next()) {
112 nsAutoPtr
<ProcessLockTable
>& table
= iter
.Data();
114 if (table
->Get(childID
, nullptr)) {
115 table
->Remove(childID
);
117 LockCount totalCount
;
118 CountWakeLocks(table
, &totalCount
);
120 if (sActiveListeners
) {
121 NotifyWakeLockChange(
122 WakeLockInfoFromLockCount(iter
.Key(), totalCount
));
125 if (totalCount
.numLocks
== 0) {
131 NS_WARNING("ipc:content-shutdown message without childID property");
142 void WakeLockInit() {
143 sLockTable
= new LockTable();
145 nsCOMPtr
<nsIObserverService
> obs
= mozilla::services::GetObserverService();
147 obs
->AddObserver(new ClearHashtableOnShutdown(), "xpcom-shutdown", false);
148 obs
->AddObserver(new CleanupOnContentShutdown(), "ipc:content-shutdown",
153 WakeLockState
ComputeWakeLockState(int aNumLocks
, int aNumHidden
) {
154 if (aNumLocks
== 0) {
155 return WAKE_LOCK_STATE_UNLOCKED
;
156 } else if (aNumLocks
== aNumHidden
) {
157 return WAKE_LOCK_STATE_HIDDEN
;
159 return WAKE_LOCK_STATE_VISIBLE
;
167 void EnableWakeLockNotifications() { sActiveListeners
++; }
169 void DisableWakeLockNotifications() { sActiveListeners
--; }
171 void ModifyWakeLock(const nsAString
& aTopic
, hal::WakeLockControl aLockAdjust
,
172 hal::WakeLockControl aHiddenAdjust
, uint64_t aProcessID
) {
173 MOZ_ASSERT(NS_IsMainThread());
174 MOZ_ASSERT(aProcessID
!= CONTENT_PROCESS_ID_UNKNOWN
);
176 if (sIsShuttingDown
) {
180 ProcessLockTable
* table
= sLockTable
->Get(aTopic
);
181 LockCount processCount
;
182 LockCount totalCount
;
184 table
= new ProcessLockTable();
185 sLockTable
->Put(aTopic
, table
);
187 table
->Get(aProcessID
, &processCount
);
188 CountWakeLocks(table
, &totalCount
);
191 MOZ_ASSERT(processCount
.numLocks
>= processCount
.numHidden
);
192 MOZ_ASSERT(aLockAdjust
>= 0 || processCount
.numLocks
> 0);
193 MOZ_ASSERT(aHiddenAdjust
>= 0 || processCount
.numHidden
> 0);
194 MOZ_ASSERT(totalCount
.numLocks
>= totalCount
.numHidden
);
195 MOZ_ASSERT(aLockAdjust
>= 0 || totalCount
.numLocks
> 0);
196 MOZ_ASSERT(aHiddenAdjust
>= 0 || totalCount
.numHidden
> 0);
198 WakeLockState oldState
=
199 ComputeWakeLockState(totalCount
.numLocks
, totalCount
.numHidden
);
200 bool processWasLocked
= processCount
.numLocks
> 0;
202 processCount
.numLocks
+= aLockAdjust
;
203 processCount
.numHidden
+= aHiddenAdjust
;
205 totalCount
.numLocks
+= aLockAdjust
;
206 totalCount
.numHidden
+= aHiddenAdjust
;
208 if (processCount
.numLocks
) {
209 table
->Put(aProcessID
, processCount
);
211 table
->Remove(aProcessID
);
213 if (!totalCount
.numLocks
) {
214 sLockTable
->Remove(aTopic
);
217 if (sActiveListeners
&&
219 ComputeWakeLockState(totalCount
.numLocks
, totalCount
.numHidden
) ||
220 processWasLocked
!= (processCount
.numLocks
> 0))) {
221 WakeLockInformation info
;
222 hal::GetWakeLockInfo(aTopic
, &info
);
223 NotifyWakeLockChange(info
);
227 void GetWakeLockInfo(const nsAString
& aTopic
,
228 WakeLockInformation
* aWakeLockInfo
) {
229 if (sIsShuttingDown
) {
231 "You don't want to get wake lock information during xpcom-shutdown!");
232 *aWakeLockInfo
= WakeLockInformation();
236 ProcessLockTable
* table
= sLockTable
->Get(aTopic
);
238 *aWakeLockInfo
= WakeLockInfoFromLockCount(aTopic
, LockCount());
241 LockCount totalCount
;
242 CountWakeLocks(table
, &totalCount
);
243 *aWakeLockInfo
= WakeLockInfoFromLockCount(aTopic
, totalCount
);
246 } // namespace hal_impl
247 } // namespace mozilla