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
;
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;
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
49 WakeLockInformation info
;
50 info
.topic() = aTopic
;
51 info
.numLocks() = aLockCount
.numLocks
;
52 info
.numHidden() = aLockCount
.numHidden
;
53 info
.lockingProcesses().AppendElements(aLockCount
.processes
);
58 CountWakeLocks(const uint64_t& aKey
, LockCount aCount
, void* 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
);
74 static PLDHashOperator
75 RemoveChildFromList(const nsAString
& aKey
, nsAutoPtr
<ProcessLockTable
>& aTable
,
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
);
86 aTable
->EnumerateRead(CountWakeLocks
, &totalCount
);
87 if (!totalCount
.numLocks
) {
91 if (sActiveListeners
) {
92 NotifyWakeLockChange(WakeLockInfoFromLockCount(aKey
, totalCount
));
99 class ClearHashtableOnShutdown MOZ_FINAL
: public nsIObserver
{
100 ~ClearHashtableOnShutdown() {}
106 NS_IMPL_ISUPPORTS(ClearHashtableOnShutdown
, nsIObserver
)
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;
119 class CleanupOnContentShutdown MOZ_FINAL
: public nsIObserver
{
120 ~CleanupOnContentShutdown() {}
126 NS_IMPL_ISUPPORTS(CleanupOnContentShutdown
, nsIObserver
)
129 CleanupOnContentShutdown::Observe(nsISupports
* aSubject
, const char* aTopic
, const char16_t
* data
)
131 MOZ_ASSERT(!strcmp(aTopic
, "ipc:content-shutdown"));
133 if (sIsShuttingDown
) {
137 nsCOMPtr
<nsIPropertyBag2
> props
= do_QueryInterface(aSubject
);
139 NS_WARNING("ipc:content-shutdown message without property bag as subject");
143 uint64_t childID
= 0;
144 nsresult rv
= props
->GetPropertyAsUint64(NS_LITERAL_STRING("childID"),
146 if (NS_SUCCEEDED(rv
)) {
147 sLockTable
->Enumerate(RemoveChildFromList
, &childID
);
149 NS_WARNING("ipc:content-shutdown message without childID property");
157 sLockTable
= new LockTable();
160 nsCOMPtr
<nsIObserverService
> obs
= mozilla::services::GetObserverService();
162 obs
->AddObserver(new ClearHashtableOnShutdown(), "xpcom-shutdown", false);
163 obs
->AddObserver(new CleanupOnContentShutdown(), "ipc:content-shutdown", false);
167 } // anonymous namespace
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
;
181 return WAKE_LOCK_STATE_VISIBLE
;
190 EnableWakeLockNotifications()
196 DisableWakeLockNotifications()
202 ModifyWakeLock(const nsAString
& aTopic
,
203 hal::WakeLockControl aLockAdjust
,
204 hal::WakeLockControl aHiddenAdjust
,
207 MOZ_ASSERT(NS_IsMainThread());
208 MOZ_ASSERT(aProcessID
!= CONTENT_PROCESS_ID_UNKNOWN
);
210 if (sIsShuttingDown
) {
217 ProcessLockTable
* table
= sLockTable
->Get(aTopic
);
218 LockCount processCount
;
219 LockCount totalCount
;
221 table
= new ProcessLockTable();
222 sLockTable
->Put(aTopic
, table
);
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
);
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
);
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();
276 ProcessLockTable
* table
= sLockTable
->Get(aTopic
);
278 *aWakeLockInfo
= WakeLockInfoFromLockCount(aTopic
, LockCount());
281 LockCount totalCount
;
282 table
->EnumerateRead(CountWakeLocks
, &totalCount
);
283 *aWakeLockInfo
= WakeLockInfoFromLockCount(aTopic
, totalCount
);