Source code

Revision control

Other Tools

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