1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set sw=2 ts=8 et ft=cpp : */
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/. */
11 #include "HalSandbox.h"
12 #include "HalWakeLockInternal.h"
13 #include "mozilla/dom/Document.h"
14 #include "nsXULAppAPI.h"
15 #include "nsPIDOMWindow.h"
16 #include "mozilla/Observer.h"
17 #include "mozilla/dom/ContentChild.h"
18 #include "WindowIdentifier.h"
22 # define getpid _getpid
25 using namespace mozilla::services
;
26 using namespace mozilla::dom
;
28 #define PROXY_IF_SANDBOXED(_call) \
31 if (!hal_sandbox::HalChildDestroyed()) { \
39 #define RETURN_PROXY_IF_SANDBOXED(_call, defValue) \
42 if (hal_sandbox::HalChildDestroyed()) { \
45 return hal_sandbox::_call; \
47 return hal_impl::_call; \
51 namespace mozilla::hal
{
53 static bool sInitialized
= false;
55 mozilla::LogModule
* GetHalLog() {
56 static mozilla::LazyLogModule
sHalLog("hal");
62 void AssertMainThread() { MOZ_ASSERT(NS_IsMainThread()); }
64 bool InSandbox() { return GeckoProcessType_Content
== XRE_GetProcessType(); }
66 bool WindowIsActive(nsPIDOMWindowInner
* aWindow
) {
67 dom::Document
* document
= aWindow
->GetDoc();
68 NS_ENSURE_TRUE(document
, false);
69 return !document
->Hidden();
72 StaticAutoPtr
<WindowIdentifier::IDArrayType
> gLastIDToVibrate
;
74 static void RecordLastIDToVibrate(const WindowIdentifier
& aId
) {
76 *gLastIDToVibrate
= aId
.AsArray().Clone();
80 static bool MayCancelVibration(const WindowIdentifier
& aId
) {
81 // Although only active windows may start vibrations, a window may
82 // cancel its own vibration even if it's no longer active.
84 // After a window is marked as inactive, it sends a CancelVibrate
85 // request. We want this request to cancel a playing vibration
86 // started by that window, so we certainly don't want to reject the
87 // cancellation request because the window is now inactive.
89 // But it could be the case that, after this window became inactive,
90 // some other window came along and started a vibration. We don't
91 // want this window's cancellation request to cancel that window's
92 // actively-playing vibration!
94 // To solve this problem, we keep track of the id of the last window
95 // to start a vibration, and only accepts cancellation requests from
96 // the same window. All other cancellation requests are ignored.
98 return InSandbox() || (*gLastIDToVibrate
== aId
.AsArray());
103 void Vibrate(const nsTArray
<uint32_t>& pattern
, nsPIDOMWindowInner
* window
) {
104 Vibrate(pattern
, WindowIdentifier(window
));
107 void Vibrate(const nsTArray
<uint32_t>& pattern
, WindowIdentifier
&& id
) {
110 // Only active windows may start vibrations. If |id| hasn't gone
111 // through the IPC layer -- that is, if our caller is the outside
112 // world, not hal_proxy -- check whether the window is active. If
113 // |id| has gone through IPC, don't check the window's visibility;
114 // only the window corresponding to the bottommost process has its
115 // visibility state set correctly.
116 if (!id
.HasTraveledThroughIPC() && !WindowIsActive(id
.GetWindow())) {
117 HAL_LOG("Vibrate: Window is inactive, dropping vibrate.");
121 RecordLastIDToVibrate(id
);
123 // Don't forward our ID if we are not in the sandbox, because hal_impl
124 // doesn't need it, and we don't want it to be tempted to read it. The
125 // empty identifier will assert if it's used.
127 Vibrate(pattern
, InSandbox() ? std::move(id
) : WindowIdentifier()));
130 void CancelVibrate(nsPIDOMWindowInner
* window
) {
131 CancelVibrate(WindowIdentifier(window
));
134 void CancelVibrate(WindowIdentifier
&& id
) {
137 if (MayCancelVibration(id
)) {
138 // Don't forward our ID if we are not in the sandbox, because hal_impl
139 // doesn't need it, and we don't want it to be tempted to read it. The
140 // empty identifier will assert if it's used.
142 CancelVibrate(InSandbox() ? std::move(id
) : WindowIdentifier()));
146 template <class InfoType
>
147 class ObserversManager
{
149 void AddObserver(Observer
<InfoType
>* aObserver
) {
150 mObservers
.AddObserver(aObserver
);
152 if (mObservers
.Length() == 1) {
153 EnableNotifications();
157 void RemoveObserver(Observer
<InfoType
>* aObserver
) {
158 bool removed
= mObservers
.RemoveObserver(aObserver
);
163 if (mObservers
.Length() == 0) {
164 DisableNotifications();
165 OnNotificationsDisabled();
169 void BroadcastInformation(const InfoType
& aInfo
) {
170 mObservers
.Broadcast(aInfo
);
174 ~ObserversManager() { MOZ_ASSERT(mObservers
.Length() == 0); }
176 virtual void EnableNotifications() = 0;
177 virtual void DisableNotifications() = 0;
178 virtual void OnNotificationsDisabled() {}
181 mozilla::ObserverList
<InfoType
> mObservers
;
184 template <class InfoType
>
185 class CachingObserversManager
: public ObserversManager
<InfoType
> {
187 InfoType
GetCurrentInformation() {
188 if (mHasValidCache
) {
192 GetCurrentInformationInternal(&mInfo
);
193 mHasValidCache
= true;
197 void CacheInformation(const InfoType
& aInfo
) {
198 mHasValidCache
= true;
202 void BroadcastCachedInformation() { this->BroadcastInformation(mInfo
); }
205 virtual void GetCurrentInformationInternal(InfoType
*) = 0;
207 void OnNotificationsDisabled() override
{ mHasValidCache
= false; }
214 class BatteryObserversManager final
215 : public CachingObserversManager
<BatteryInformation
> {
217 void EnableNotifications() override
{
218 PROXY_IF_SANDBOXED(EnableBatteryNotifications());
221 void DisableNotifications() override
{
222 PROXY_IF_SANDBOXED(DisableBatteryNotifications());
225 void GetCurrentInformationInternal(BatteryInformation
* aInfo
) override
{
226 PROXY_IF_SANDBOXED(GetCurrentBatteryInformation(aInfo
));
230 class NetworkObserversManager final
231 : public CachingObserversManager
<NetworkInformation
> {
233 void EnableNotifications() override
{
234 PROXY_IF_SANDBOXED(EnableNetworkNotifications());
237 void DisableNotifications() override
{
238 PROXY_IF_SANDBOXED(DisableNetworkNotifications());
241 void GetCurrentInformationInternal(NetworkInformation
* aInfo
) override
{
242 PROXY_IF_SANDBOXED(GetCurrentNetworkInformation(aInfo
));
246 class WakeLockObserversManager final
247 : public ObserversManager
<WakeLockInformation
> {
249 void EnableNotifications() override
{
250 PROXY_IF_SANDBOXED(EnableWakeLockNotifications());
253 void DisableNotifications() override
{
254 PROXY_IF_SANDBOXED(DisableWakeLockNotifications());
258 typedef mozilla::ObserverList
<SensorData
> SensorObserverList
;
259 StaticAutoPtr
<SensorObserverList
> sSensorObservers
[NUM_SENSOR_TYPE
];
261 static SensorObserverList
* GetSensorObservers(SensorType sensor_type
) {
263 MOZ_ASSERT(sensor_type
< NUM_SENSOR_TYPE
);
265 if (!sSensorObservers
[sensor_type
]) {
266 sSensorObservers
[sensor_type
] = new SensorObserverList();
269 return sSensorObservers
[sensor_type
];
272 #define MOZ_IMPL_HAL_OBSERVER(name_) \
273 StaticAutoPtr<name_##ObserversManager> s##name_##Observers; \
275 static name_##ObserversManager* name_##Observers() { \
276 AssertMainThread(); \
278 if (!s##name_##Observers) { \
279 MOZ_ASSERT(sInitialized); \
280 s##name_##Observers = new name_##ObserversManager(); \
283 return s##name_##Observers; \
286 void Register##name_##Observer(name_##Observer* aObserver) { \
287 AssertMainThread(); \
288 name_##Observers()->AddObserver(aObserver); \
291 void Unregister##name_##Observer(name_##Observer* aObserver) { \
292 AssertMainThread(); \
293 name_##Observers()->RemoveObserver(aObserver); \
296 MOZ_IMPL_HAL_OBSERVER(Battery
)
298 void GetCurrentBatteryInformation(BatteryInformation
* aInfo
) {
299 *aInfo
= BatteryObservers()->GetCurrentInformation();
302 void NotifyBatteryChange(const BatteryInformation
& aInfo
) {
303 BatteryObservers()->CacheInformation(aInfo
);
304 BatteryObservers()->BroadcastCachedInformation();
307 void EnableSensorNotifications(SensorType aSensor
) {
309 PROXY_IF_SANDBOXED(EnableSensorNotifications(aSensor
));
312 void DisableSensorNotifications(SensorType aSensor
) {
314 PROXY_IF_SANDBOXED(DisableSensorNotifications(aSensor
));
317 void RegisterSensorObserver(SensorType aSensor
, ISensorObserver
* aObserver
) {
318 SensorObserverList
* observers
= GetSensorObservers(aSensor
);
320 observers
->AddObserver(aObserver
);
321 if (observers
->Length() == 1) {
322 EnableSensorNotifications(aSensor
);
326 void UnregisterSensorObserver(SensorType aSensor
, ISensorObserver
* aObserver
) {
327 SensorObserverList
* observers
= GetSensorObservers(aSensor
);
328 if (!observers
->RemoveObserver(aObserver
) || observers
->Length() > 0) {
331 DisableSensorNotifications(aSensor
);
334 void NotifySensorChange(const SensorData
& aSensorData
) {
335 SensorObserverList
* observers
= GetSensorObservers(aSensorData
.sensor());
337 observers
->Broadcast(aSensorData
);
340 MOZ_IMPL_HAL_OBSERVER(Network
)
342 void GetCurrentNetworkInformation(NetworkInformation
* aInfo
) {
343 *aInfo
= NetworkObservers()->GetCurrentInformation();
346 void NotifyNetworkChange(const NetworkInformation
& aInfo
) {
347 NetworkObservers()->CacheInformation(aInfo
);
348 NetworkObservers()->BroadcastCachedInformation();
351 MOZ_IMPL_HAL_OBSERVER(WakeLock
)
353 void ModifyWakeLock(const nsAString
& aTopic
, WakeLockControl aLockAdjust
,
354 WakeLockControl aHiddenAdjust
,
355 uint64_t aProcessID
/* = CONTENT_PROCESS_ID_UNKNOWN */) {
358 if (aProcessID
== CONTENT_PROCESS_ID_UNKNOWN
) {
359 aProcessID
= InSandbox() ? ContentChild::GetSingleton()->GetID()
360 : CONTENT_PROCESS_ID_MAIN
;
364 ModifyWakeLock(aTopic
, aLockAdjust
, aHiddenAdjust
, aProcessID
));
367 void GetWakeLockInfo(const nsAString
& aTopic
,
368 WakeLockInformation
* aWakeLockInfo
) {
370 PROXY_IF_SANDBOXED(GetWakeLockInfo(aTopic
, aWakeLockInfo
));
373 void NotifyWakeLockChange(const WakeLockInformation
& aInfo
) {
375 WakeLockObservers()->BroadcastInformation(aInfo
);
378 RefPtr
<GenericNonExclusivePromise
> LockScreenOrientation(
379 const ScreenOrientation
& aOrientation
) {
381 RETURN_PROXY_IF_SANDBOXED(LockScreenOrientation(aOrientation
), nullptr);
384 void UnlockScreenOrientation() {
386 PROXY_IF_SANDBOXED(UnlockScreenOrientation());
389 void SetProcessPriority(int aPid
, ProcessPriority aPriority
) {
390 // n.b. The sandboxed implementation crashes; SetProcessPriority works only
391 // from the main process.
392 PROXY_IF_SANDBOXED(SetProcessPriority(aPid
, aPriority
));
396 const char* ProcessPriorityToString(ProcessPriority aPriority
) {
398 case PROCESS_PRIORITY_PARENT_PROCESS
:
399 return "PARENT_PROCESS";
400 case PROCESS_PRIORITY_PREALLOC
:
402 case PROCESS_PRIORITY_FOREGROUND_HIGH
:
403 return "FOREGROUND_HIGH";
404 case PROCESS_PRIORITY_FOREGROUND
:
406 case PROCESS_PRIORITY_FOREGROUND_KEYBOARD
:
407 return "FOREGROUND_KEYBOARD";
408 case PROCESS_PRIORITY_BACKGROUND_PERCEIVABLE
:
409 return "BACKGROUND_PERCEIVABLE";
410 case PROCESS_PRIORITY_BACKGROUND
:
412 case PROCESS_PRIORITY_UNKNOWN
:
420 UniquePtr
<hal::PerformanceHintSession
> CreatePerformanceHintSession(
421 const nsTArray
<PlatformThreadHandle
>& aThreads
,
422 mozilla::TimeDuration aTargetWorkDuration
) {
423 return hal_impl::CreatePerformanceHintSession(aThreads
, aTargetWorkDuration
);
426 const Maybe
<hal::HeterogeneousCpuInfo
>& GetHeterogeneousCpuInfo() {
427 return hal_impl::GetHeterogeneousCpuInfo();
431 MOZ_ASSERT(!sInitialized
);
434 gLastIDToVibrate
= new WindowIdentifier::IDArrayType();
443 MOZ_ASSERT(sInitialized
);
445 gLastIDToVibrate
= nullptr;
447 sBatteryObservers
= nullptr;
448 sNetworkObservers
= nullptr;
449 sWakeLockObservers
= nullptr;
451 for (auto& sensorObserver
: sSensorObservers
) {
452 sensorObserver
= nullptr;
455 sInitialized
= false;
458 } // namespace mozilla::hal