1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/hal/HalWakeLock.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,285 @@ 1.4 +/* -*- Mode: C++; tab-width: 40; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this file, 1.7 + * You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +#include "mozilla/Hal.h" 1.10 +#include "mozilla/HalWakeLock.h" 1.11 +#include "mozilla/Services.h" 1.12 +#include "mozilla/StaticPtr.h" 1.13 +#include "mozilla/dom/ContentParent.h" 1.14 +#include "nsClassHashtable.h" 1.15 +#include "nsDataHashtable.h" 1.16 +#include "nsHashKeys.h" 1.17 +#include "nsIPropertyBag2.h" 1.18 +#include "nsIObserverService.h" 1.19 + 1.20 +using namespace mozilla; 1.21 +using namespace mozilla::hal; 1.22 + 1.23 +namespace { 1.24 + 1.25 +struct LockCount { 1.26 + LockCount() 1.27 + : numLocks(0) 1.28 + , numHidden(0) 1.29 + {} 1.30 + uint32_t numLocks; 1.31 + uint32_t numHidden; 1.32 + nsTArray<uint64_t> processes; 1.33 +}; 1.34 + 1.35 +typedef nsDataHashtable<nsUint64HashKey, LockCount> ProcessLockTable; 1.36 +typedef nsClassHashtable<nsStringHashKey, ProcessLockTable> LockTable; 1.37 + 1.38 +int sActiveListeners = 0; 1.39 +StaticAutoPtr<LockTable> sLockTable; 1.40 +bool sInitialized = false; 1.41 +bool sIsShuttingDown = false; 1.42 + 1.43 +WakeLockInformation 1.44 +WakeLockInfoFromLockCount(const nsAString& aTopic, const LockCount& aLockCount) 1.45 +{ 1.46 + // TODO: Once we abandon b2g18, we can switch this to use the 1.47 + // WakeLockInformation constructor, which is better because it doesn't let us 1.48 + // forget to assign a param. For now we have to do it this way, because 1.49 + // b2g18 doesn't have the nsTArray <--> InfallibleTArray conversion (bug 1.50 + // 819791). 1.51 + 1.52 + WakeLockInformation info; 1.53 + info.topic() = aTopic; 1.54 + info.numLocks() = aLockCount.numLocks; 1.55 + info.numHidden() = aLockCount.numHidden; 1.56 + info.lockingProcesses().AppendElements(aLockCount.processes); 1.57 + return info; 1.58 +} 1.59 + 1.60 +PLDHashOperator 1.61 +CountWakeLocks(const uint64_t& aKey, LockCount aCount, void* aUserArg) 1.62 +{ 1.63 + MOZ_ASSERT(aUserArg); 1.64 + 1.65 + LockCount* totalCount = static_cast<LockCount*>(aUserArg); 1.66 + totalCount->numLocks += aCount.numLocks; 1.67 + totalCount->numHidden += aCount.numHidden; 1.68 + 1.69 + // This is linear in the number of processes, but that should be small. 1.70 + if (!totalCount->processes.Contains(aKey)) { 1.71 + totalCount->processes.AppendElement(aKey); 1.72 + } 1.73 + 1.74 + return PL_DHASH_NEXT; 1.75 +} 1.76 + 1.77 +static PLDHashOperator 1.78 +RemoveChildFromList(const nsAString& aKey, nsAutoPtr<ProcessLockTable>& aTable, 1.79 + void* aUserArg) 1.80 +{ 1.81 + MOZ_ASSERT(aUserArg); 1.82 + 1.83 + PLDHashOperator op = PL_DHASH_NEXT; 1.84 + uint64_t childID = *static_cast<uint64_t*>(aUserArg); 1.85 + if (aTable->Get(childID, nullptr)) { 1.86 + aTable->Remove(childID); 1.87 + 1.88 + LockCount totalCount; 1.89 + aTable->EnumerateRead(CountWakeLocks, &totalCount); 1.90 + if (!totalCount.numLocks) { 1.91 + op = PL_DHASH_REMOVE; 1.92 + } 1.93 + 1.94 + if (sActiveListeners) { 1.95 + NotifyWakeLockChange(WakeLockInfoFromLockCount(aKey, totalCount)); 1.96 + } 1.97 + } 1.98 + 1.99 + return op; 1.100 +} 1.101 + 1.102 +class ClearHashtableOnShutdown MOZ_FINAL : public nsIObserver { 1.103 +public: 1.104 + NS_DECL_ISUPPORTS 1.105 + NS_DECL_NSIOBSERVER 1.106 +}; 1.107 + 1.108 +NS_IMPL_ISUPPORTS(ClearHashtableOnShutdown, nsIObserver) 1.109 + 1.110 +NS_IMETHODIMP 1.111 +ClearHashtableOnShutdown::Observe(nsISupports* aSubject, const char* aTopic, const char16_t* data) 1.112 +{ 1.113 + MOZ_ASSERT(!strcmp(aTopic, "xpcom-shutdown")); 1.114 + 1.115 + sIsShuttingDown = true; 1.116 + sLockTable = nullptr; 1.117 + 1.118 + return NS_OK; 1.119 +} 1.120 + 1.121 +class CleanupOnContentShutdown MOZ_FINAL : public nsIObserver { 1.122 +public: 1.123 + NS_DECL_ISUPPORTS 1.124 + NS_DECL_NSIOBSERVER 1.125 +}; 1.126 + 1.127 +NS_IMPL_ISUPPORTS(CleanupOnContentShutdown, nsIObserver) 1.128 + 1.129 +NS_IMETHODIMP 1.130 +CleanupOnContentShutdown::Observe(nsISupports* aSubject, const char* aTopic, const char16_t* data) 1.131 +{ 1.132 + MOZ_ASSERT(!strcmp(aTopic, "ipc:content-shutdown")); 1.133 + 1.134 + if (sIsShuttingDown) { 1.135 + return NS_OK; 1.136 + } 1.137 + 1.138 + nsCOMPtr<nsIPropertyBag2> props = do_QueryInterface(aSubject); 1.139 + if (!props) { 1.140 + NS_WARNING("ipc:content-shutdown message without property bag as subject"); 1.141 + return NS_OK; 1.142 + } 1.143 + 1.144 + uint64_t childID = 0; 1.145 + nsresult rv = props->GetPropertyAsUint64(NS_LITERAL_STRING("childID"), 1.146 + &childID); 1.147 + if (NS_SUCCEEDED(rv)) { 1.148 + sLockTable->Enumerate(RemoveChildFromList, &childID); 1.149 + } else { 1.150 + NS_WARNING("ipc:content-shutdown message without childID property"); 1.151 + } 1.152 + return NS_OK; 1.153 +} 1.154 + 1.155 +void 1.156 +Init() 1.157 +{ 1.158 + sLockTable = new LockTable(); 1.159 + sInitialized = true; 1.160 + 1.161 + nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService(); 1.162 + if (obs) { 1.163 + obs->AddObserver(new ClearHashtableOnShutdown(), "xpcom-shutdown", false); 1.164 + obs->AddObserver(new CleanupOnContentShutdown(), "ipc:content-shutdown", false); 1.165 + } 1.166 +} 1.167 + 1.168 +} // anonymous namespace 1.169 + 1.170 +namespace mozilla { 1.171 + 1.172 +namespace hal { 1.173 + 1.174 +WakeLockState 1.175 +ComputeWakeLockState(int aNumLocks, int aNumHidden) 1.176 +{ 1.177 + if (aNumLocks == 0) { 1.178 + return WAKE_LOCK_STATE_UNLOCKED; 1.179 + } else if (aNumLocks == aNumHidden) { 1.180 + return WAKE_LOCK_STATE_HIDDEN; 1.181 + } else { 1.182 + return WAKE_LOCK_STATE_VISIBLE; 1.183 + } 1.184 +} 1.185 + 1.186 +} // namespace hal 1.187 + 1.188 +namespace hal_impl { 1.189 + 1.190 +void 1.191 +EnableWakeLockNotifications() 1.192 +{ 1.193 + sActiveListeners++; 1.194 +} 1.195 + 1.196 +void 1.197 +DisableWakeLockNotifications() 1.198 +{ 1.199 + sActiveListeners--; 1.200 +} 1.201 + 1.202 +void 1.203 +ModifyWakeLock(const nsAString& aTopic, 1.204 + hal::WakeLockControl aLockAdjust, 1.205 + hal::WakeLockControl aHiddenAdjust, 1.206 + uint64_t aProcessID) 1.207 +{ 1.208 + MOZ_ASSERT(NS_IsMainThread()); 1.209 + MOZ_ASSERT(aProcessID != CONTENT_PROCESS_ID_UNKNOWN); 1.210 + 1.211 + if (sIsShuttingDown) { 1.212 + return; 1.213 + } 1.214 + if (!sInitialized) { 1.215 + Init(); 1.216 + } 1.217 + 1.218 + ProcessLockTable* table = sLockTable->Get(aTopic); 1.219 + LockCount processCount; 1.220 + LockCount totalCount; 1.221 + if (!table) { 1.222 + table = new ProcessLockTable(); 1.223 + sLockTable->Put(aTopic, table); 1.224 + } else { 1.225 + table->Get(aProcessID, &processCount); 1.226 + table->EnumerateRead(CountWakeLocks, &totalCount); 1.227 + } 1.228 + 1.229 + MOZ_ASSERT(processCount.numLocks >= processCount.numHidden); 1.230 + MOZ_ASSERT(aLockAdjust >= 0 || processCount.numLocks > 0); 1.231 + MOZ_ASSERT(aHiddenAdjust >= 0 || processCount.numHidden > 0); 1.232 + MOZ_ASSERT(totalCount.numLocks >= totalCount.numHidden); 1.233 + MOZ_ASSERT(aLockAdjust >= 0 || totalCount.numLocks > 0); 1.234 + MOZ_ASSERT(aHiddenAdjust >= 0 || totalCount.numHidden > 0); 1.235 + 1.236 + WakeLockState oldState = ComputeWakeLockState(totalCount.numLocks, totalCount.numHidden); 1.237 + bool processWasLocked = processCount.numLocks > 0; 1.238 + 1.239 + processCount.numLocks += aLockAdjust; 1.240 + processCount.numHidden += aHiddenAdjust; 1.241 + 1.242 + totalCount.numLocks += aLockAdjust; 1.243 + totalCount.numHidden += aHiddenAdjust; 1.244 + 1.245 + if (processCount.numLocks) { 1.246 + table->Put(aProcessID, processCount); 1.247 + } else { 1.248 + table->Remove(aProcessID); 1.249 + } 1.250 + if (!totalCount.numLocks) { 1.251 + sLockTable->Remove(aTopic); 1.252 + } 1.253 + 1.254 + if (sActiveListeners && 1.255 + (oldState != ComputeWakeLockState(totalCount.numLocks, 1.256 + totalCount.numHidden) || 1.257 + processWasLocked != (processCount.numLocks > 0))) { 1.258 + 1.259 + WakeLockInformation info; 1.260 + hal::GetWakeLockInfo(aTopic, &info); 1.261 + NotifyWakeLockChange(info); 1.262 + } 1.263 +} 1.264 + 1.265 +void 1.266 +GetWakeLockInfo(const nsAString& aTopic, WakeLockInformation* aWakeLockInfo) 1.267 +{ 1.268 + if (sIsShuttingDown) { 1.269 + NS_WARNING("You don't want to get wake lock information during xpcom-shutdown!"); 1.270 + *aWakeLockInfo = WakeLockInformation(); 1.271 + return; 1.272 + } 1.273 + if (!sInitialized) { 1.274 + Init(); 1.275 + } 1.276 + 1.277 + ProcessLockTable* table = sLockTable->Get(aTopic); 1.278 + if (!table) { 1.279 + *aWakeLockInfo = WakeLockInfoFromLockCount(aTopic, LockCount()); 1.280 + return; 1.281 + } 1.282 + LockCount totalCount; 1.283 + table->EnumerateRead(CountWakeLocks, &totalCount); 1.284 + *aWakeLockInfo = WakeLockInfoFromLockCount(aTopic, totalCount); 1.285 +} 1.286 + 1.287 +} // hal_impl 1.288 +} // mozilla