summaryrefslogtreecommitdiffstats
path: root/hal/Hal.cpp
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--hal/Hal.cpp452
1 files changed, 452 insertions, 0 deletions
diff --git a/hal/Hal.cpp b/hal/Hal.cpp
new file mode 100644
index 0000000000..c8e25690bc
--- /dev/null
+++ b/hal/Hal.cpp
@@ -0,0 +1,452 @@
+/* -*- 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) {
+ NS_ENSURE_TRUE(aWindow, false);
+ 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) {
+ AssertMainThread();
+
+ PROXY_IF_SANDBOXED(ModifyWakeLock(aTopic, aLockAdjust, aHiddenAdjust));
+}
+
+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 "???";
+ }
+}
+
+UniquePtr<hal::PerformanceHintSession> CreatePerformanceHintSession(
+ const nsTArray<PlatformThreadHandle>& aThreads,
+ mozilla::TimeDuration aTargetWorkDuration) {
+ return hal_impl::CreatePerformanceHintSession(aThreads, aTargetWorkDuration);
+}
+
+const Maybe<hal::HeterogeneousCpuInfo>& GetHeterogeneousCpuInfo() {
+ return hal_impl::GetHeterogeneousCpuInfo();
+}
+
+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