diff options
Diffstat (limited to 'hal/Hal.cpp')
-rw-r--r-- | hal/Hal.cpp | 448 |
1 files changed, 448 insertions, 0 deletions
diff --git a/hal/Hal.cpp b/hal/Hal.cpp new file mode 100644 index 0000000000..911f3e5012 --- /dev/null +++ b/hal/Hal.cpp @@ -0,0 +1,448 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set sw=2 ts=8 et ft=cpp : */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "Hal.h" + +#include "HalImpl.h" +#include "HalLog.h" +#include "HalSandbox.h" +#include "HalWakeLockInternal.h" +#include "mozilla/dom/Document.h" +#include "nsXULAppAPI.h" +#include "nsPIDOMWindow.h" +#include "mozilla/Observer.h" +#include "mozilla/dom/ContentChild.h" +#include "WindowIdentifier.h" + +#ifdef XP_WIN +# include <process.h> +# define getpid _getpid +#endif + +using namespace mozilla::services; +using namespace mozilla::dom; + +#define PROXY_IF_SANDBOXED(_call) \ + do { \ + if (InSandbox()) { \ + if (!hal_sandbox::HalChildDestroyed()) { \ + hal_sandbox::_call; \ + } \ + } else { \ + hal_impl::_call; \ + } \ + } while (0) + +#define RETURN_PROXY_IF_SANDBOXED(_call, defValue) \ + do { \ + if (InSandbox()) { \ + if (hal_sandbox::HalChildDestroyed()) { \ + return defValue; \ + } \ + return hal_sandbox::_call; \ + } else { \ + return hal_impl::_call; \ + } \ + } while (0) + +namespace mozilla::hal { + +static bool sInitialized = false; + +mozilla::LogModule* GetHalLog() { + static mozilla::LazyLogModule sHalLog("hal"); + return sHalLog; +} + +namespace { + +void AssertMainThread() { MOZ_ASSERT(NS_IsMainThread()); } + +bool InSandbox() { return GeckoProcessType_Content == XRE_GetProcessType(); } + +bool WindowIsActive(nsPIDOMWindowInner* aWindow) { + dom::Document* document = aWindow->GetDoc(); + NS_ENSURE_TRUE(document, false); + return !document->Hidden(); +} + +StaticAutoPtr<WindowIdentifier::IDArrayType> gLastIDToVibrate; + +static void RecordLastIDToVibrate(const WindowIdentifier& aId) { + if (!InSandbox()) { + *gLastIDToVibrate = aId.AsArray().Clone(); + } +} + +static bool MayCancelVibration(const WindowIdentifier& aId) { + // Although only active windows may start vibrations, a window may + // cancel its own vibration even if it's no longer active. + // + // After a window is marked as inactive, it sends a CancelVibrate + // request. We want this request to cancel a playing vibration + // started by that window, so we certainly don't want to reject the + // cancellation request because the window is now inactive. + // + // But it could be the case that, after this window became inactive, + // some other window came along and started a vibration. We don't + // want this window's cancellation request to cancel that window's + // actively-playing vibration! + // + // To solve this problem, we keep track of the id of the last window + // to start a vibration, and only accepts cancellation requests from + // the same window. All other cancellation requests are ignored. + + return InSandbox() || (*gLastIDToVibrate == aId.AsArray()); +} + +} // namespace + +void Vibrate(const nsTArray<uint32_t>& pattern, nsPIDOMWindowInner* window) { + Vibrate(pattern, WindowIdentifier(window)); +} + +void Vibrate(const nsTArray<uint32_t>& pattern, WindowIdentifier&& id) { + AssertMainThread(); + + // Only active windows may start vibrations. If |id| hasn't gone + // through the IPC layer -- that is, if our caller is the outside + // world, not hal_proxy -- check whether the window is active. If + // |id| has gone through IPC, don't check the window's visibility; + // only the window corresponding to the bottommost process has its + // visibility state set correctly. + if (!id.HasTraveledThroughIPC() && !WindowIsActive(id.GetWindow())) { + HAL_LOG("Vibrate: Window is inactive, dropping vibrate."); + return; + } + + RecordLastIDToVibrate(id); + + // Don't forward our ID if we are not in the sandbox, because hal_impl + // doesn't need it, and we don't want it to be tempted to read it. The + // empty identifier will assert if it's used. + PROXY_IF_SANDBOXED( + Vibrate(pattern, InSandbox() ? std::move(id) : WindowIdentifier())); +} + +void CancelVibrate(nsPIDOMWindowInner* window) { + CancelVibrate(WindowIdentifier(window)); +} + +void CancelVibrate(WindowIdentifier&& id) { + AssertMainThread(); + + if (MayCancelVibration(id)) { + // Don't forward our ID if we are not in the sandbox, because hal_impl + // doesn't need it, and we don't want it to be tempted to read it. The + // empty identifier will assert if it's used. + PROXY_IF_SANDBOXED( + CancelVibrate(InSandbox() ? std::move(id) : WindowIdentifier())); + } +} + +template <class InfoType> +class ObserversManager { + public: + void AddObserver(Observer<InfoType>* aObserver) { + mObservers.AddObserver(aObserver); + + if (mObservers.Length() == 1) { + EnableNotifications(); + } + } + + void RemoveObserver(Observer<InfoType>* aObserver) { + bool removed = mObservers.RemoveObserver(aObserver); + if (!removed) { + return; + } + + if (mObservers.Length() == 0) { + DisableNotifications(); + OnNotificationsDisabled(); + } + } + + void BroadcastInformation(const InfoType& aInfo) { + mObservers.Broadcast(aInfo); + } + + protected: + ~ObserversManager() { MOZ_ASSERT(mObservers.Length() == 0); } + + virtual void EnableNotifications() = 0; + virtual void DisableNotifications() = 0; + virtual void OnNotificationsDisabled() {} + + private: + mozilla::ObserverList<InfoType> mObservers; +}; + +template <class InfoType> +class CachingObserversManager : public ObserversManager<InfoType> { + public: + InfoType GetCurrentInformation() { + if (mHasValidCache) { + return mInfo; + } + + GetCurrentInformationInternal(&mInfo); + mHasValidCache = true; + return mInfo; + } + + void CacheInformation(const InfoType& aInfo) { + mHasValidCache = true; + mInfo = aInfo; + } + + void BroadcastCachedInformation() { this->BroadcastInformation(mInfo); } + + protected: + virtual void GetCurrentInformationInternal(InfoType*) = 0; + + void OnNotificationsDisabled() override { mHasValidCache = false; } + + private: + InfoType mInfo; + bool mHasValidCache; +}; + +class BatteryObserversManager final + : public CachingObserversManager<BatteryInformation> { + protected: + void EnableNotifications() override { + PROXY_IF_SANDBOXED(EnableBatteryNotifications()); + } + + void DisableNotifications() override { + PROXY_IF_SANDBOXED(DisableBatteryNotifications()); + } + + void GetCurrentInformationInternal(BatteryInformation* aInfo) override { + PROXY_IF_SANDBOXED(GetCurrentBatteryInformation(aInfo)); + } +}; + +class NetworkObserversManager final + : public CachingObserversManager<NetworkInformation> { + protected: + void EnableNotifications() override { + PROXY_IF_SANDBOXED(EnableNetworkNotifications()); + } + + void DisableNotifications() override { + PROXY_IF_SANDBOXED(DisableNetworkNotifications()); + } + + void GetCurrentInformationInternal(NetworkInformation* aInfo) override { + PROXY_IF_SANDBOXED(GetCurrentNetworkInformation(aInfo)); + } +}; + +class WakeLockObserversManager final + : public ObserversManager<WakeLockInformation> { + protected: + void EnableNotifications() override { + PROXY_IF_SANDBOXED(EnableWakeLockNotifications()); + } + + void DisableNotifications() override { + PROXY_IF_SANDBOXED(DisableWakeLockNotifications()); + } +}; + +typedef mozilla::ObserverList<SensorData> SensorObserverList; +StaticAutoPtr<SensorObserverList> sSensorObservers[NUM_SENSOR_TYPE]; + +static SensorObserverList* GetSensorObservers(SensorType sensor_type) { + AssertMainThread(); + MOZ_ASSERT(sensor_type < NUM_SENSOR_TYPE); + + if (!sSensorObservers[sensor_type]) { + sSensorObservers[sensor_type] = new SensorObserverList(); + } + + return sSensorObservers[sensor_type]; +} + +#define MOZ_IMPL_HAL_OBSERVER(name_) \ + StaticAutoPtr<name_##ObserversManager> s##name_##Observers; \ + \ + static name_##ObserversManager* name_##Observers() { \ + AssertMainThread(); \ + \ + if (!s##name_##Observers) { \ + MOZ_ASSERT(sInitialized); \ + s##name_##Observers = new name_##ObserversManager(); \ + } \ + \ + return s##name_##Observers; \ + } \ + \ + void Register##name_##Observer(name_##Observer* aObserver) { \ + AssertMainThread(); \ + name_##Observers()->AddObserver(aObserver); \ + } \ + \ + void Unregister##name_##Observer(name_##Observer* aObserver) { \ + AssertMainThread(); \ + name_##Observers()->RemoveObserver(aObserver); \ + } + +MOZ_IMPL_HAL_OBSERVER(Battery) + +void GetCurrentBatteryInformation(BatteryInformation* aInfo) { + *aInfo = BatteryObservers()->GetCurrentInformation(); +} + +void NotifyBatteryChange(const BatteryInformation& aInfo) { + BatteryObservers()->CacheInformation(aInfo); + BatteryObservers()->BroadcastCachedInformation(); +} + +void EnableSensorNotifications(SensorType aSensor) { + AssertMainThread(); + PROXY_IF_SANDBOXED(EnableSensorNotifications(aSensor)); +} + +void DisableSensorNotifications(SensorType aSensor) { + AssertMainThread(); + PROXY_IF_SANDBOXED(DisableSensorNotifications(aSensor)); +} + +void RegisterSensorObserver(SensorType aSensor, ISensorObserver* aObserver) { + SensorObserverList* observers = GetSensorObservers(aSensor); + + observers->AddObserver(aObserver); + if (observers->Length() == 1) { + EnableSensorNotifications(aSensor); + } +} + +void UnregisterSensorObserver(SensorType aSensor, ISensorObserver* aObserver) { + SensorObserverList* observers = GetSensorObservers(aSensor); + if (!observers->RemoveObserver(aObserver) || observers->Length() > 0) { + return; + } + DisableSensorNotifications(aSensor); +} + +void NotifySensorChange(const SensorData& aSensorData) { + SensorObserverList* observers = GetSensorObservers(aSensorData.sensor()); + + observers->Broadcast(aSensorData); +} + +MOZ_IMPL_HAL_OBSERVER(Network) + +void GetCurrentNetworkInformation(NetworkInformation* aInfo) { + *aInfo = NetworkObservers()->GetCurrentInformation(); +} + +void NotifyNetworkChange(const NetworkInformation& aInfo) { + NetworkObservers()->CacheInformation(aInfo); + NetworkObservers()->BroadcastCachedInformation(); +} + +MOZ_IMPL_HAL_OBSERVER(WakeLock) + +void ModifyWakeLock(const nsAString& aTopic, WakeLockControl aLockAdjust, + WakeLockControl aHiddenAdjust, + uint64_t aProcessID /* = CONTENT_PROCESS_ID_UNKNOWN */) { + AssertMainThread(); + + if (aProcessID == CONTENT_PROCESS_ID_UNKNOWN) { + aProcessID = InSandbox() ? ContentChild::GetSingleton()->GetID() + : CONTENT_PROCESS_ID_MAIN; + } + + PROXY_IF_SANDBOXED( + ModifyWakeLock(aTopic, aLockAdjust, aHiddenAdjust, aProcessID)); +} + +void GetWakeLockInfo(const nsAString& aTopic, + WakeLockInformation* aWakeLockInfo) { + AssertMainThread(); + PROXY_IF_SANDBOXED(GetWakeLockInfo(aTopic, aWakeLockInfo)); +} + +void NotifyWakeLockChange(const WakeLockInformation& aInfo) { + AssertMainThread(); + WakeLockObservers()->BroadcastInformation(aInfo); +} + +RefPtr<GenericNonExclusivePromise> LockScreenOrientation( + const ScreenOrientation& aOrientation) { + AssertMainThread(); + RETURN_PROXY_IF_SANDBOXED(LockScreenOrientation(aOrientation), nullptr); +} + +void UnlockScreenOrientation() { + AssertMainThread(); + PROXY_IF_SANDBOXED(UnlockScreenOrientation()); +} + +void SetProcessPriority(int aPid, ProcessPriority aPriority) { + // n.b. The sandboxed implementation crashes; SetProcessPriority works only + // from the main process. + PROXY_IF_SANDBOXED(SetProcessPriority(aPid, aPriority)); +} + +// From HalTypes.h. +const char* ProcessPriorityToString(ProcessPriority aPriority) { + switch (aPriority) { + case PROCESS_PRIORITY_PARENT_PROCESS: + return "PARENT_PROCESS"; + case PROCESS_PRIORITY_PREALLOC: + return "PREALLOC"; + case PROCESS_PRIORITY_FOREGROUND_HIGH: + return "FOREGROUND_HIGH"; + case PROCESS_PRIORITY_FOREGROUND: + return "FOREGROUND"; + case PROCESS_PRIORITY_FOREGROUND_KEYBOARD: + return "FOREGROUND_KEYBOARD"; + case PROCESS_PRIORITY_BACKGROUND_PERCEIVABLE: + return "BACKGROUND_PERCEIVABLE"; + case PROCESS_PRIORITY_BACKGROUND: + return "BACKGROUND"; + case PROCESS_PRIORITY_UNKNOWN: + return "UNKNOWN"; + default: + MOZ_ASSERT(false); + return "???"; + } +} + +void Init() { + MOZ_ASSERT(!sInitialized); + + if (!InSandbox()) { + gLastIDToVibrate = new WindowIdentifier::IDArrayType(); + } + + WakeLockInit(); + + sInitialized = true; +} + +void Shutdown() { + MOZ_ASSERT(sInitialized); + + gLastIDToVibrate = nullptr; + + sBatteryObservers = nullptr; + sNetworkObservers = nullptr; + sWakeLockObservers = nullptr; + + for (auto& sensorObserver : sSensorObservers) { + sensorObserver = nullptr; + } + + sInitialized = false; +} + +} // namespace mozilla::hal |