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 "base/process_util.h"
9 #include "mozilla/FOGIPC.h"
10 #include "mozilla/HalWakeLock.h"
11 #include "mozilla/Services.h"
12 #include "mozilla/StaticPtr.h"
13 #include "nsClassHashtable.h"
14 #include "nsTHashMap.h"
15 #include "nsHashKeys.h"
16 #include "nsIPropertyBag2.h"
17 #include "nsIObserver.h"
18 #include "nsIObserverService.h"
20 using namespace mozilla
;
21 using namespace mozilla::hal
;
26 LockCount() : numLocks(0), numHidden(0) {}
29 CopyableTArray
<uint64_t> processes
;
32 typedef nsTHashMap
<nsUint64HashKey
, LockCount
> ProcessLockTable
;
33 typedef nsClassHashtable
<nsStringHashKey
, ProcessLockTable
> LockTable
;
35 int sActiveListeners
= 0;
36 StaticAutoPtr
<LockTable
> sLockTable
;
37 bool sIsShuttingDown
= false;
39 WakeLockInformation
WakeLockInfoFromLockCount(const nsAString
& aTopic
,
40 const LockCount
& aLockCount
) {
41 nsString
topic(aTopic
);
42 WakeLockInformation
info(topic
, aLockCount
.numLocks
, aLockCount
.numHidden
,
43 aLockCount
.processes
);
48 static void CountWakeLocks(ProcessLockTable
* aTable
, LockCount
* aTotalCount
) {
49 for (auto iter
= aTable
->Iter(); !iter
.Done(); iter
.Next()) {
50 const uint64_t& key
= iter
.Key();
51 LockCount count
= iter
.UserData();
53 aTotalCount
->numLocks
+= count
.numLocks
;
54 aTotalCount
->numHidden
+= count
.numHidden
;
56 // This is linear in the number of processes, but that should be small.
57 if (!aTotalCount
->processes
.Contains(key
)) {
58 aTotalCount
->processes
.AppendElement(key
);
63 class ClearHashtableOnShutdown final
: public nsIObserver
{
64 ~ClearHashtableOnShutdown() {}
71 NS_IMPL_ISUPPORTS(ClearHashtableOnShutdown
, nsIObserver
)
74 ClearHashtableOnShutdown::Observe(nsISupports
* aSubject
, const char* aTopic
,
75 const char16_t
* data
) {
76 MOZ_ASSERT(!strcmp(aTopic
, "xpcom-shutdown"));
78 sIsShuttingDown
= true;
84 class CleanupOnContentShutdown final
: public nsIObserver
{
85 ~CleanupOnContentShutdown() {}
92 NS_IMPL_ISUPPORTS(CleanupOnContentShutdown
, nsIObserver
)
95 CleanupOnContentShutdown::Observe(nsISupports
* aSubject
, const char* aTopic
,
96 const char16_t
* data
) {
97 MOZ_ASSERT(!strcmp(aTopic
, "ipc:content-shutdown"));
99 if (sIsShuttingDown
) {
103 nsCOMPtr
<nsIPropertyBag2
> props
= do_QueryInterface(aSubject
);
105 NS_WARNING("ipc:content-shutdown message without property bag as subject");
109 uint64_t childID
= 0;
110 nsresult rv
= props
->GetPropertyAsUint64(u
"childID"_ns
, &childID
);
111 if (NS_SUCCEEDED(rv
)) {
112 for (auto iter
= sLockTable
->Iter(); !iter
.Done(); iter
.Next()) {
113 auto table
= iter
.UserData();
115 if (table
->Get(childID
, nullptr)) {
116 table
->Remove(childID
);
118 LockCount totalCount
;
119 CountWakeLocks(table
, &totalCount
);
121 if (sActiveListeners
) {
122 NotifyWakeLockChange(
123 WakeLockInfoFromLockCount(iter
.Key(), totalCount
));
126 if (totalCount
.numLocks
== 0) {
132 NS_WARNING("ipc:content-shutdown message without childID property");
143 void WakeLockInit() {
144 sLockTable
= new LockTable();
146 nsCOMPtr
<nsIObserverService
> obs
= mozilla::services::GetObserverService();
148 obs
->AddObserver(new ClearHashtableOnShutdown(), "xpcom-shutdown", false);
149 obs
->AddObserver(new CleanupOnContentShutdown(), "ipc:content-shutdown",
154 WakeLockState
ComputeWakeLockState(int aNumLocks
, int aNumHidden
) {
155 if (aNumLocks
== 0) {
156 return WAKE_LOCK_STATE_UNLOCKED
;
157 } else if (aNumLocks
== aNumHidden
) {
158 return WAKE_LOCK_STATE_HIDDEN
;
160 return WAKE_LOCK_STATE_VISIBLE
;
168 void EnableWakeLockNotifications() { sActiveListeners
++; }
170 void DisableWakeLockNotifications() { sActiveListeners
--; }
172 void ModifyWakeLockWithChildID(const nsAString
& aTopic
,
173 hal::WakeLockControl aLockAdjust
,
174 hal::WakeLockControl aHiddenAdjust
,
176 MOZ_ASSERT(NS_IsMainThread());
177 MOZ_ASSERT(aChildID
!= CONTENT_PROCESS_ID_UNKNOWN
);
179 if (sIsShuttingDown
) {
183 LockCount processCount
;
184 LockCount totalCount
;
185 ProcessLockTable
* const table
=
186 sLockTable
->WithEntryHandle(aTopic
, [&](auto&& entry
) {
188 entry
.Insert(MakeUnique
<ProcessLockTable
>());
190 Unused
<< entry
.Data()->Get(aChildID
, &processCount
);
191 CountWakeLocks(entry
->get(), &totalCount
);
196 MOZ_ASSERT(processCount
.numLocks
>= processCount
.numHidden
);
197 MOZ_ASSERT(aLockAdjust
>= 0 || processCount
.numLocks
> 0);
198 MOZ_ASSERT(aHiddenAdjust
>= 0 || processCount
.numHidden
> 0);
199 MOZ_ASSERT(totalCount
.numLocks
>= totalCount
.numHidden
);
200 MOZ_ASSERT(aLockAdjust
>= 0 || totalCount
.numLocks
> 0);
201 MOZ_ASSERT(aHiddenAdjust
>= 0 || totalCount
.numHidden
> 0);
203 WakeLockState oldState
=
204 ComputeWakeLockState(totalCount
.numLocks
, totalCount
.numHidden
);
206 if (ComputeWakeLockState(totalCount
.numLocks
+ aLockAdjust
,
207 totalCount
.numHidden
+ aHiddenAdjust
) != oldState
&&
208 (aTopic
.Equals(u
"video-playing"_ns
) ||
209 aTopic
.Equals(u
"audio-playing"_ns
))) {
210 glean::RecordPowerMetrics();
213 bool processWasLocked
= processCount
.numLocks
> 0;
215 processCount
.numLocks
+= aLockAdjust
;
216 processCount
.numHidden
+= aHiddenAdjust
;
218 totalCount
.numLocks
+= aLockAdjust
;
219 totalCount
.numHidden
+= aHiddenAdjust
;
221 if (processCount
.numLocks
) {
222 table
->InsertOrUpdate(aChildID
, processCount
);
224 table
->Remove(aChildID
);
226 if (!totalCount
.numLocks
) {
227 sLockTable
->Remove(aTopic
);
230 if (sActiveListeners
&&
232 ComputeWakeLockState(totalCount
.numLocks
, totalCount
.numHidden
) ||
233 processWasLocked
!= (processCount
.numLocks
> 0))) {
234 WakeLockInformation info
;
235 hal::GetWakeLockInfo(aTopic
, &info
);
236 NotifyWakeLockChange(info
);
240 void ModifyWakeLock(const nsAString
& aTopic
, hal::WakeLockControl aLockAdjust
,
241 hal::WakeLockControl aHiddenAdjust
) {
242 ModifyWakeLockWithChildID(aTopic
, aLockAdjust
, aHiddenAdjust
,
243 CONTENT_PROCESS_ID_MAIN
);
246 void GetWakeLockInfo(const nsAString
& aTopic
,
247 WakeLockInformation
* aWakeLockInfo
) {
248 if (sIsShuttingDown
) {
250 "You don't want to get wake lock information during xpcom-shutdown!");
251 *aWakeLockInfo
= WakeLockInformation();
256 // This can happen during some gtests.
257 NS_WARNING("Attempting to get wake lock information before initialization");
258 *aWakeLockInfo
= WakeLockInformation();
262 ProcessLockTable
* table
= sLockTable
->Get(aTopic
);
264 *aWakeLockInfo
= WakeLockInfoFromLockCount(aTopic
, LockCount());
267 LockCount totalCount
;
268 CountWakeLocks(table
, &totalCount
);
269 *aWakeLockInfo
= WakeLockInfoFromLockCount(aTopic
, totalCount
);
272 } // namespace hal_impl
273 } // namespace mozilla