summaryrefslogtreecommitdiffstats
path: root/hal
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
commit36d22d82aa202bb199967e9512281e9a53db42c9 (patch)
tree105e8c98ddea1c1e4784a60a5a6410fa416be2de /hal
parentInitial commit. (diff)
downloadfirefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.tar.xz
firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.zip
Adding upstream version 115.7.0esr.upstream/115.7.0esrupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'hal')
-rw-r--r--hal/Hal.cpp448
-rw-r--r--hal/Hal.h247
-rw-r--r--hal/HalBatteryInformation.h19
-rw-r--r--hal/HalIPCUtils.h23
-rw-r--r--hal/HalImpl.h21
-rw-r--r--hal/HalInternal.h65
-rw-r--r--hal/HalLog.h33
-rw-r--r--hal/HalNetworkInformation.h19
-rw-r--r--hal/HalSandbox.h17
-rw-r--r--hal/HalScreenConfiguration.h37
-rw-r--r--hal/HalSensor.h56
-rw-r--r--hal/HalTypes.h83
-rw-r--r--hal/HalWakeLock.cpp265
-rw-r--r--hal/HalWakeLock.h26
-rw-r--r--hal/HalWakeLockInformation.h18
-rw-r--r--hal/HalWakeLockInternal.h17
-rw-r--r--hal/WindowIdentifier.cpp61
-rw-r--r--hal/WindowIdentifier.h106
-rw-r--r--hal/android/AndroidHal.cpp158
-rw-r--r--hal/android/AndroidProcessPriority.cpp62
-rw-r--r--hal/android/AndroidSensor.cpp23
-rw-r--r--hal/cocoa/CocoaBattery.cpp317
-rw-r--r--hal/cocoa/CocoaSensor.mm137
-rw-r--r--hal/cocoa/smslib.h158
-rw-r--r--hal/cocoa/smslib.mm870
-rw-r--r--hal/fallback/FallbackBattery.cpp24
-rw-r--r--hal/fallback/FallbackNetwork.cpp25
-rw-r--r--hal/fallback/FallbackProcessPriority.cpp19
-rw-r--r--hal/fallback/FallbackScreenConfiguration.cpp17
-rw-r--r--hal/fallback/FallbackSensor.cpp19
-rw-r--r--hal/fallback/FallbackVibration.cpp19
-rw-r--r--hal/linux/LinuxProcessPriority.cpp78
-rw-r--r--hal/linux/UPowerClient.cpp448
-rw-r--r--hal/moz.build136
-rw-r--r--hal/sandbox/PHal.ipdl99
-rw-r--r--hal/sandbox/SandboxHal.cpp341
-rw-r--r--hal/sandbox/SandboxHal.h23
-rw-r--r--hal/windows/WindowsBattery.cpp115
-rw-r--r--hal/windows/WindowsProcessPriority.cpp85
-rw-r--r--hal/windows/WindowsScreenConfiguration.cpp103
-rw-r--r--hal/windows/WindowsSensor.cpp170
41 files changed, 5007 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
diff --git a/hal/Hal.h b/hal/Hal.h
new file mode 100644
index 0000000000..7125370057
--- /dev/null
+++ b/hal/Hal.h
@@ -0,0 +1,247 @@
+/* -*- 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/. */
+
+#ifndef mozilla_Hal_h
+#define mozilla_Hal_h
+
+#include "nsTArray.h"
+#include "mozilla/hal_sandbox/PHal.h"
+#include "mozilla/HalScreenConfiguration.h"
+#include "mozilla/HalBatteryInformation.h"
+#include "mozilla/HalNetworkInformation.h"
+#include "mozilla/HalWakeLockInformation.h"
+#include "mozilla/HalTypes.h"
+#include "mozilla/MozPromise.h"
+
+#include <cstdint>
+
+/*
+ * Hal.h contains the public Hal API.
+ *
+ * By default, this file defines its functions in the hal namespace, but if
+ * MOZ_HAL_NAMESPACE is defined, we'll define our functions in that namespace.
+ *
+ * This is used by HalImpl.h and HalSandbox.h, which define copies of all the
+ * functions here in the hal_impl and hal_sandbox namespaces.
+ */
+
+class nsPIDOMWindowInner;
+
+#ifndef MOZ_HAL_NAMESPACE
+# define MOZ_HAL_NAMESPACE hal
+# define MOZ_DEFINED_HAL_NAMESPACE 1
+#endif
+
+namespace mozilla {
+
+namespace hal {
+
+class WindowIdentifier;
+
+} // namespace hal
+
+namespace MOZ_HAL_NAMESPACE {
+
+/**
+ * Initializes the HAL. This must be called before any other HAL function.
+ */
+void Init();
+
+/**
+ * Shuts down the HAL. Besides freeing all the used resources this will check
+ * that all observers have been properly deregistered and assert if not.
+ */
+void Shutdown();
+
+/**
+ * Turn the default vibrator device on/off per the pattern specified
+ * by |pattern|. Each element in the pattern is the number of
+ * milliseconds to turn the vibrator on or off. The first element in
+ * |pattern| is an "on" element, the next is "off", and so on.
+ *
+ * If |pattern| is empty, any in-progress vibration is canceled.
+ *
+ * Only an active window within an active tab may call Vibrate; calls
+ * from inactive windows and windows on inactive tabs do nothing.
+ *
+ * If you're calling hal::Vibrate from the outside world, pass an
+ * nsIDOMWindow* in place of the WindowIdentifier parameter.
+ * The method with WindowIdentifier will be called automatically.
+ */
+void Vibrate(const nsTArray<uint32_t>& pattern, nsPIDOMWindowInner* aWindow);
+void Vibrate(const nsTArray<uint32_t>& pattern, hal::WindowIdentifier&& id);
+
+/**
+ * Cancel a vibration started by the content window identified by
+ * WindowIdentifier.
+ *
+ * If the window was the last window to start a vibration, the
+ * cancellation request will go through even if the window is not
+ * active.
+ *
+ * As with hal::Vibrate(), if you're calling hal::CancelVibrate from the outside
+ * world, pass an nsIDOMWindow*. The method with WindowIdentifier will be called
+ * automatically.
+ */
+void CancelVibrate(nsPIDOMWindowInner* aWindow);
+void CancelVibrate(hal::WindowIdentifier&& id);
+
+#define MOZ_DEFINE_HAL_OBSERVER(name_) \
+ /** \
+ * Inform the backend there is a new |name_| observer. \
+ * @param aObserver The observer that should be added. \
+ */ \
+ void Register##name_##Observer(hal::name_##Observer* aObserver); \
+ /** \
+ * Inform the backend a |name_| observer unregistered. \
+ * @param aObserver The observer that should be removed. \
+ */ \
+ void Unregister##name_##Observer(hal::name_##Observer* aObserver);
+
+MOZ_DEFINE_HAL_OBSERVER(Battery);
+
+/**
+ * Returns the current battery information.
+ */
+void GetCurrentBatteryInformation(hal::BatteryInformation* aBatteryInfo);
+
+/**
+ * Notify of a change in the battery state.
+ * @param aBatteryInfo The new battery information.
+ */
+void NotifyBatteryChange(const hal::BatteryInformation& aBatteryInfo);
+
+/**
+ * Register an observer for the sensor of given type.
+ *
+ * The observer will receive data whenever the data generated by the
+ * sensor is avaiable.
+ */
+void RegisterSensorObserver(hal::SensorType aSensor,
+ hal::ISensorObserver* aObserver);
+
+/**
+ * Unregister an observer for the sensor of given type.
+ */
+void UnregisterSensorObserver(hal::SensorType aSensor,
+ hal::ISensorObserver* aObserver);
+
+/**
+ * Post a value generated by a sensor.
+ *
+ * This API is internal to hal; clients shouldn't call it directly.
+ */
+void NotifySensorChange(const hal::SensorData& aSensorData);
+
+/**
+ * Enable sensor notifications from the backend
+ *
+ * This method is only visible from implementation of sensor manager.
+ * Rest of the system should not try this.
+ */
+void EnableSensorNotifications(hal::SensorType aSensor);
+
+/**
+ * Disable sensor notifications from the backend
+ *
+ * This method is only visible from implementation of sensor manager.
+ * Rest of the system should not try this.
+ */
+void DisableSensorNotifications(hal::SensorType aSensor);
+
+MOZ_DEFINE_HAL_OBSERVER(Network);
+
+/**
+ * Returns the current network information.
+ */
+void GetCurrentNetworkInformation(hal::NetworkInformation* aNetworkInfo);
+
+/**
+ * Notify of a change in the network state.
+ * @param aNetworkInfo The new network information.
+ */
+void NotifyNetworkChange(const hal::NetworkInformation& aNetworkInfo);
+
+/**
+ * Enable wake lock notifications from the backend.
+ *
+ * This method is only used by WakeLockObserversManager.
+ */
+void EnableWakeLockNotifications();
+
+/**
+ * Disable wake lock notifications from the backend.
+ *
+ * This method is only used by WakeLockObserversManager.
+ */
+void DisableWakeLockNotifications();
+
+MOZ_DEFINE_HAL_OBSERVER(WakeLock);
+
+/**
+ * Adjust a wake lock's counts on behalf of a given process.
+ *
+ * In most cases, you shouldn't need to pass the aProcessID argument; the
+ * default of CONTENT_PROCESS_ID_UNKNOWN is probably what you want.
+ *
+ * @param aTopic lock topic
+ * @param aLockAdjust to increase or decrease active locks
+ * @param aHiddenAdjust to increase or decrease hidden locks
+ * @param aProcessID indicates which process we're modifying the wake lock
+ * on behalf of. It is interpreted as
+ *
+ * CONTENT_PROCESS_ID_UNKNOWN: The current process
+ * CONTENT_PROCESS_ID_MAIN: The root process
+ * X: The process with ContentChild::GetID() == X
+ */
+void ModifyWakeLock(const nsAString& aTopic, hal::WakeLockControl aLockAdjust,
+ hal::WakeLockControl aHiddenAdjust,
+ uint64_t aProcessID = hal::CONTENT_PROCESS_ID_UNKNOWN);
+
+/**
+ * Query the wake lock numbers of aTopic.
+ * @param aTopic lock topic
+ * @param aWakeLockInfo wake lock numbers
+ */
+void GetWakeLockInfo(const nsAString& aTopic,
+ hal::WakeLockInformation* aWakeLockInfo);
+
+/**
+ * Notify of a change in the wake lock state.
+ * @param aWakeLockInfo The new wake lock information.
+ */
+void NotifyWakeLockChange(const hal::WakeLockInformation& aWakeLockInfo);
+
+/**
+ * Lock the screen orientation to the specific orientation.
+ * @return A promise indicating that the screen orientation has been locked.
+ */
+[[nodiscard]] RefPtr<GenericNonExclusivePromise> LockScreenOrientation(
+ const hal::ScreenOrientation& aOrientation);
+
+/**
+ * Unlock the screen orientation.
+ */
+void UnlockScreenOrientation();
+
+/**
+ * Set the priority of the given process.
+ *
+ * Exactly what this does will vary between platforms. On *nix we might give
+ * background processes higher nice values. On other platforms, we might
+ * ignore this call entirely.
+ */
+void SetProcessPriority(int aPid, hal::ProcessPriority aPriority);
+
+} // namespace MOZ_HAL_NAMESPACE
+} // namespace mozilla
+
+#ifdef MOZ_DEFINED_HAL_NAMESPACE
+# undef MOZ_DEFINED_HAL_NAMESPACE
+# undef MOZ_HAL_NAMESPACE
+#endif
+
+#endif // mozilla_Hal_h
diff --git a/hal/HalBatteryInformation.h b/hal/HalBatteryInformation.h
new file mode 100644
index 0000000000..8e992ef442
--- /dev/null
+++ b/hal/HalBatteryInformation.h
@@ -0,0 +1,19 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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/. */
+
+#ifndef mozilla_HalBatteryInformation_h
+#define mozilla_HalBatteryInformation_h
+
+#include "mozilla/Observer.h"
+
+namespace mozilla {
+namespace hal {
+class BatteryInformation;
+typedef Observer<hal::BatteryInformation> BatteryObserver;
+} // namespace hal
+} // namespace mozilla
+
+#endif // mozilla_HalBatteryInformation_h
diff --git a/hal/HalIPCUtils.h b/hal/HalIPCUtils.h
new file mode 100644
index 0000000000..5a3f15e573
--- /dev/null
+++ b/hal/HalIPCUtils.h
@@ -0,0 +1,23 @@
+/* -*- 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/. */
+
+#ifndef mozilla_HalIPCUtils_h
+#define mozilla_HalIPCUtils_h
+
+#include "HalScreenConfiguration.h"
+
+#include "ipc/EnumSerializer.h"
+
+namespace IPC {
+
+template <>
+struct ParamTraits<mozilla::hal::ScreenOrientation>
+ : public BitFlagsEnumSerializer<mozilla::hal::ScreenOrientation,
+ mozilla::hal::kAllScreenOrientationBits> {};
+
+} // namespace IPC
+
+#endif
diff --git a/hal/HalImpl.h b/hal/HalImpl.h
new file mode 100644
index 0000000000..eed2492812
--- /dev/null
+++ b/hal/HalImpl.h
@@ -0,0 +1,21 @@
+/* -*- 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/. */
+
+#ifndef mozilla_HalImpl_h
+#define mozilla_HalImpl_h
+
+#ifdef MOZ_UNIFIED_BUILD
+# error Cannot use HalImpl.h in unified builds.
+#endif
+
+#define MOZ_HAL_NAMESPACE hal_impl
+#undef mozilla_Hal_h
+#undef mozilla_HalInternal_h
+#include "Hal.h"
+#include "HalInternal.h"
+#undef MOZ_HAL_NAMESPACE
+
+#endif // mozilla_HalImpl_h
diff --git a/hal/HalInternal.h b/hal/HalInternal.h
new file mode 100644
index 0000000000..a26e0a985a
--- /dev/null
+++ b/hal/HalInternal.h
@@ -0,0 +1,65 @@
+/* -*- 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/. */
+
+#ifndef mozilla_HalInternal_h
+#define mozilla_HalInternal_h 1
+
+/*
+ * This file is included by HalImpl.h and HalSandbox.h with a mechanism similar
+ * to Hal.h. That means those headers set MOZ_HAL_NAMESPACE to specify in which
+ * namespace the internal functions should appear.
+ *
+ * The difference between Hal.h and HalInternal.h is that methods declared in
+ * HalInternal.h don't appear in the hal namespace. That also means this file
+ * should not be included except by HalImpl.h and HalSandbox.h.
+ */
+
+#ifndef MOZ_HAL_NAMESPACE
+# error "You shouldn't directly include HalInternal.h!"
+#endif
+
+namespace mozilla {
+namespace MOZ_HAL_NAMESPACE {
+
+/**
+ * Enables battery notifications from the backend.
+ */
+void EnableBatteryNotifications();
+
+/**
+ * Disables battery notifications from the backend.
+ */
+void DisableBatteryNotifications();
+
+/**
+ * Enables network notifications from the backend.
+ */
+void EnableNetworkNotifications();
+
+/**
+ * Disables network notifications from the backend.
+ */
+void DisableNetworkNotifications();
+
+/**
+ * Enables screen orientation notifications from the backend.
+ */
+void EnableScreenConfigurationNotifications();
+
+/**
+ * Disables screen orientation notifications from the backend.
+ */
+void DisableScreenConfigurationNotifications();
+
+/**
+ * Has the child-side HAL IPC object been destroyed? If so, you shouldn't send
+ * messages to hal_sandbox.
+ */
+bool HalChildDestroyed();
+} // namespace MOZ_HAL_NAMESPACE
+} // namespace mozilla
+
+#endif // mozilla_HalInternal_h
diff --git a/hal/HalLog.h b/hal/HalLog.h
new file mode 100644
index 0000000000..fcf9c5edfa
--- /dev/null
+++ b/hal/HalLog.h
@@ -0,0 +1,33 @@
+/* -*- 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/. */
+
+#ifndef mozilla_HalLog_h
+#define mozilla_HalLog_h
+
+#include "mozilla/Logging.h"
+
+/*
+ * HalLog.h contains internal macros and functions used for logging.
+ *
+ * This should be considered a private include and not used in non-HAL code.
+ * To enable logging in non-debug builds define the PR_FORCE_LOG macro here.
+ */
+
+namespace mozilla {
+
+namespace hal {
+
+mozilla::LogModule* GetHalLog();
+#define HAL_LOG(...) \
+ MOZ_LOG(mozilla::hal::GetHalLog(), LogLevel::Debug, (__VA_ARGS__))
+#define HAL_ERR(...) \
+ MOZ_LOG(mozilla::hal::GetHalLog(), LogLevel::Error, (__VA_ARGS__))
+
+} // namespace hal
+
+} // namespace mozilla
+
+#endif // mozilla_HalLog_h
diff --git a/hal/HalNetworkInformation.h b/hal/HalNetworkInformation.h
new file mode 100644
index 0000000000..9c6b334ddd
--- /dev/null
+++ b/hal/HalNetworkInformation.h
@@ -0,0 +1,19 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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/. */
+
+#ifndef mozilla_HalNetworkInformation_h
+#define mozilla_HalNetworkInformation_h
+
+#include "mozilla/Observer.h"
+
+namespace mozilla {
+namespace hal {
+class NetworkInformation;
+typedef Observer<hal::NetworkInformation> NetworkObserver;
+} // namespace hal
+} // namespace mozilla
+
+#endif // mozilla_HalNetworkInformation_h
diff --git a/hal/HalSandbox.h b/hal/HalSandbox.h
new file mode 100644
index 0000000000..a4be721ea1
--- /dev/null
+++ b/hal/HalSandbox.h
@@ -0,0 +1,17 @@
+/* -*- 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/. */
+
+#ifndef mozilla_hal_HalSandbox_h
+#define mozilla_hal_HalSandbox_h
+
+#define MOZ_HAL_NAMESPACE hal_sandbox
+#undef mozilla_Hal_h
+#undef mozilla_HalInternal_h
+#include "Hal.h"
+#include "HalInternal.h"
+#undef MOZ_HAL_NAMESPACE
+
+#endif // mozilla_hal_HalSandbox_h
diff --git a/hal/HalScreenConfiguration.h b/hal/HalScreenConfiguration.h
new file mode 100644
index 0000000000..9a577884b6
--- /dev/null
+++ b/hal/HalScreenConfiguration.h
@@ -0,0 +1,37 @@
+/* -*- 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/. */
+
+#ifndef mozilla_HalScreenConfiguration_h
+#define mozilla_HalScreenConfiguration_h
+
+#include "mozilla/Observer.h"
+#include "mozilla/TypedEnumBits.h"
+
+// Undo X11/X.h's definition of None
+#undef None
+
+namespace mozilla::hal {
+
+// Make sure that any change to ScreenOrientation values are also made in
+// mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoScreenOrientation.java
+enum class ScreenOrientation : uint32_t {
+ None = 0,
+ PortraitPrimary = 1u << 0,
+ PortraitSecondary = 1u << 1,
+ LandscapePrimary = 1u << 2,
+ LandscapeSecondary = 1u << 3,
+ // Default will use the natural orientation for the device, it could be
+ // PortraitPrimary or LandscapePrimary depends on display resolution
+ Default = 1u << 4,
+};
+
+constexpr auto kAllScreenOrientationBits = ScreenOrientation((1 << 5) - 1);
+
+MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(ScreenOrientation);
+
+} // namespace mozilla::hal
+
+#endif // mozilla_HalScreenConfiguration_h
diff --git a/hal/HalSensor.h b/hal/HalSensor.h
new file mode 100644
index 0000000000..39fc33848b
--- /dev/null
+++ b/hal/HalSensor.h
@@ -0,0 +1,56 @@
+/* -*- 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/. */
+
+#ifndef __HAL_SENSOR_H_
+#define __HAL_SENSOR_H_
+
+#include "mozilla/Observer.h"
+
+namespace mozilla {
+namespace hal {
+
+/**
+ * Enumeration of sensor types. They are used to specify type while
+ * register or unregister an observer for a sensor of given type.
+ * If you add or change any here, do the same in GeckoHalDefines.java.
+ */
+enum SensorType {
+ SENSOR_ORIENTATION = 0,
+ SENSOR_ACCELERATION = 1,
+ SENSOR_PROXIMITY = 2,
+ SENSOR_LINEAR_ACCELERATION = 3,
+ SENSOR_GYROSCOPE = 4,
+ SENSOR_LIGHT = 5,
+ SENSOR_ROTATION_VECTOR = 6,
+ SENSOR_GAME_ROTATION_VECTOR = 7,
+ NUM_SENSOR_TYPE
+};
+
+class SensorData;
+
+typedef Observer<SensorData> ISensorObserver;
+
+class SensorAccuracy;
+
+typedef Observer<SensorAccuracy> ISensorAccuracyObserver;
+
+} // namespace hal
+} // namespace mozilla
+
+#include "ipc/EnumSerializer.h"
+
+namespace IPC {
+/**
+ * Serializer for SensorType
+ */
+template <>
+struct ParamTraits<mozilla::hal::SensorType>
+ : public ContiguousEnumSerializer<mozilla::hal::SensorType,
+ mozilla::hal::SENSOR_ORIENTATION,
+ mozilla::hal::NUM_SENSOR_TYPE> {};
+} // namespace IPC
+
+#endif /* __HAL_SENSOR_H_ */
diff --git a/hal/HalTypes.h b/hal/HalTypes.h
new file mode 100644
index 0000000000..e89392f355
--- /dev/null
+++ b/hal/HalTypes.h
@@ -0,0 +1,83 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/. */
+
+#ifndef mozilla_hal_Types_h
+#define mozilla_hal_Types_h
+
+#include "ipc/EnumSerializer.h"
+#include "mozilla/Observer.h"
+
+namespace mozilla {
+namespace hal {
+
+/**
+ * These constants specify special values for content process IDs. You can get
+ * a content process ID by calling ContentChild::GetID() or
+ * ContentParent::GetChildID().
+ */
+const uint64_t CONTENT_PROCESS_ID_UNKNOWN = uint64_t(-1);
+const uint64_t CONTENT_PROCESS_ID_MAIN = 0;
+
+// Note that we rely on the order of this enum's entries. Higher priorities
+// should have larger int values.
+enum ProcessPriority {
+ PROCESS_PRIORITY_UNKNOWN = -1,
+ PROCESS_PRIORITY_BACKGROUND,
+ PROCESS_PRIORITY_BACKGROUND_PERCEIVABLE,
+ PROCESS_PRIORITY_FOREGROUND_KEYBOARD,
+ // The special class for the preallocated process, high memory priority but
+ // low CPU priority.
+ PROCESS_PRIORITY_PREALLOC,
+ // Any priority greater than or equal to FOREGROUND is considered
+ // "foreground" for the purposes of priority testing, for example
+ // CurrentProcessIsForeground().
+ PROCESS_PRIORITY_FOREGROUND,
+ PROCESS_PRIORITY_FOREGROUND_HIGH,
+ PROCESS_PRIORITY_PARENT_PROCESS,
+ NUM_PROCESS_PRIORITY
+};
+
+/**
+ * Convert a ProcessPriority enum value to a string. The strings returned by
+ * this function are statically allocated; do not attempt to free one!
+ *
+ * If you pass an unknown process priority, we fatally assert in debug
+ * builds and otherwise return "???".
+ */
+const char* ProcessPriorityToString(ProcessPriority aPriority);
+
+/**
+ * Used by ModifyWakeLock
+ */
+enum WakeLockControl {
+ WAKE_LOCK_REMOVE_ONE = -1,
+ WAKE_LOCK_NO_CHANGE = 0,
+ WAKE_LOCK_ADD_ONE = 1,
+ NUM_WAKE_LOCK
+};
+
+} // namespace hal
+} // namespace mozilla
+
+namespace IPC {
+
+/**
+ * WakeLockControl serializer.
+ */
+template <>
+struct ParamTraits<mozilla::hal::WakeLockControl>
+ : public ContiguousEnumSerializer<mozilla::hal::WakeLockControl,
+ mozilla::hal::WAKE_LOCK_REMOVE_ONE,
+ mozilla::hal::NUM_WAKE_LOCK> {};
+
+template <>
+struct ParamTraits<mozilla::hal::ProcessPriority>
+ : public ContiguousEnumSerializer<mozilla::hal::ProcessPriority,
+ mozilla::hal::PROCESS_PRIORITY_UNKNOWN,
+ mozilla::hal::NUM_PROCESS_PRIORITY> {};
+
+} // namespace IPC
+
+#endif // mozilla_hal_Types_h
diff --git a/hal/HalWakeLock.cpp b/hal/HalWakeLock.cpp
new file mode 100644
index 0000000000..918f4b2da6
--- /dev/null
+++ b/hal/HalWakeLock.cpp
@@ -0,0 +1,265 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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 "base/process_util.h"
+#include "mozilla/FOGIPC.h"
+#include "mozilla/HalWakeLock.h"
+#include "mozilla/Services.h"
+#include "mozilla/StaticPtr.h"
+#include "nsClassHashtable.h"
+#include "nsTHashMap.h"
+#include "nsHashKeys.h"
+#include "nsIPropertyBag2.h"
+#include "nsIObserver.h"
+#include "nsIObserverService.h"
+
+using namespace mozilla;
+using namespace mozilla::hal;
+
+namespace {
+
+struct LockCount {
+ LockCount() : numLocks(0), numHidden(0) {}
+ uint32_t numLocks;
+ uint32_t numHidden;
+ CopyableTArray<uint64_t> processes;
+};
+
+typedef nsTHashMap<nsUint64HashKey, LockCount> ProcessLockTable;
+typedef nsClassHashtable<nsStringHashKey, ProcessLockTable> LockTable;
+
+int sActiveListeners = 0;
+StaticAutoPtr<LockTable> sLockTable;
+bool sIsShuttingDown = false;
+
+WakeLockInformation WakeLockInfoFromLockCount(const nsAString& aTopic,
+ const LockCount& aLockCount) {
+ nsString topic(aTopic);
+ WakeLockInformation info(topic, aLockCount.numLocks, aLockCount.numHidden,
+ aLockCount.processes);
+
+ return info;
+}
+
+static void CountWakeLocks(ProcessLockTable* aTable, LockCount* aTotalCount) {
+ for (auto iter = aTable->Iter(); !iter.Done(); iter.Next()) {
+ const uint64_t& key = iter.Key();
+ LockCount count = iter.UserData();
+
+ aTotalCount->numLocks += count.numLocks;
+ aTotalCount->numHidden += count.numHidden;
+
+ // This is linear in the number of processes, but that should be small.
+ if (!aTotalCount->processes.Contains(key)) {
+ aTotalCount->processes.AppendElement(key);
+ }
+ }
+}
+
+class ClearHashtableOnShutdown final : public nsIObserver {
+ ~ClearHashtableOnShutdown() {}
+
+ public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIOBSERVER
+};
+
+NS_IMPL_ISUPPORTS(ClearHashtableOnShutdown, nsIObserver)
+
+NS_IMETHODIMP
+ClearHashtableOnShutdown::Observe(nsISupports* aSubject, const char* aTopic,
+ const char16_t* data) {
+ MOZ_ASSERT(!strcmp(aTopic, "xpcom-shutdown"));
+
+ sIsShuttingDown = true;
+ sLockTable = nullptr;
+
+ return NS_OK;
+}
+
+class CleanupOnContentShutdown final : public nsIObserver {
+ ~CleanupOnContentShutdown() {}
+
+ public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIOBSERVER
+};
+
+NS_IMPL_ISUPPORTS(CleanupOnContentShutdown, nsIObserver)
+
+NS_IMETHODIMP
+CleanupOnContentShutdown::Observe(nsISupports* aSubject, const char* aTopic,
+ const char16_t* data) {
+ MOZ_ASSERT(!strcmp(aTopic, "ipc:content-shutdown"));
+
+ if (sIsShuttingDown) {
+ return NS_OK;
+ }
+
+ nsCOMPtr<nsIPropertyBag2> props = do_QueryInterface(aSubject);
+ if (!props) {
+ NS_WARNING("ipc:content-shutdown message without property bag as subject");
+ return NS_OK;
+ }
+
+ uint64_t childID = 0;
+ nsresult rv = props->GetPropertyAsUint64(u"childID"_ns, &childID);
+ if (NS_SUCCEEDED(rv)) {
+ for (auto iter = sLockTable->Iter(); !iter.Done(); iter.Next()) {
+ auto table = iter.UserData();
+
+ if (table->Get(childID, nullptr)) {
+ table->Remove(childID);
+
+ LockCount totalCount;
+ CountWakeLocks(table, &totalCount);
+
+ if (sActiveListeners) {
+ NotifyWakeLockChange(
+ WakeLockInfoFromLockCount(iter.Key(), totalCount));
+ }
+
+ if (totalCount.numLocks == 0) {
+ iter.Remove();
+ }
+ }
+ }
+ } else {
+ NS_WARNING("ipc:content-shutdown message without childID property");
+ }
+ return NS_OK;
+}
+
+} // namespace
+
+namespace mozilla {
+
+namespace hal {
+
+void WakeLockInit() {
+ sLockTable = new LockTable();
+
+ nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
+ if (obs) {
+ obs->AddObserver(new ClearHashtableOnShutdown(), "xpcom-shutdown", false);
+ obs->AddObserver(new CleanupOnContentShutdown(), "ipc:content-shutdown",
+ false);
+ }
+}
+
+WakeLockState ComputeWakeLockState(int aNumLocks, int aNumHidden) {
+ if (aNumLocks == 0) {
+ return WAKE_LOCK_STATE_UNLOCKED;
+ } else if (aNumLocks == aNumHidden) {
+ return WAKE_LOCK_STATE_HIDDEN;
+ } else {
+ return WAKE_LOCK_STATE_VISIBLE;
+ }
+}
+
+} // namespace hal
+
+namespace hal_impl {
+
+void EnableWakeLockNotifications() { sActiveListeners++; }
+
+void DisableWakeLockNotifications() { sActiveListeners--; }
+
+void ModifyWakeLock(const nsAString& aTopic, hal::WakeLockControl aLockAdjust,
+ hal::WakeLockControl aHiddenAdjust, uint64_t aProcessID) {
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(aProcessID != CONTENT_PROCESS_ID_UNKNOWN);
+
+ if (sIsShuttingDown) {
+ return;
+ }
+
+ LockCount processCount;
+ LockCount totalCount;
+ ProcessLockTable* const table =
+ sLockTable->WithEntryHandle(aTopic, [&](auto&& entry) {
+ if (!entry) {
+ entry.Insert(MakeUnique<ProcessLockTable>());
+ } else {
+ Unused << entry.Data()->Get(aProcessID, &processCount);
+ CountWakeLocks(entry->get(), &totalCount);
+ }
+ return entry->get();
+ });
+
+ MOZ_ASSERT(processCount.numLocks >= processCount.numHidden);
+ MOZ_ASSERT(aLockAdjust >= 0 || processCount.numLocks > 0);
+ MOZ_ASSERT(aHiddenAdjust >= 0 || processCount.numHidden > 0);
+ MOZ_ASSERT(totalCount.numLocks >= totalCount.numHidden);
+ MOZ_ASSERT(aLockAdjust >= 0 || totalCount.numLocks > 0);
+ MOZ_ASSERT(aHiddenAdjust >= 0 || totalCount.numHidden > 0);
+
+ WakeLockState oldState =
+ ComputeWakeLockState(totalCount.numLocks, totalCount.numHidden);
+
+ if (ComputeWakeLockState(totalCount.numLocks + aLockAdjust,
+ totalCount.numHidden + aHiddenAdjust) != oldState &&
+ (aTopic.Equals(u"video-playing"_ns) ||
+ aTopic.Equals(u"audio-playing"_ns))) {
+ glean::RecordPowerMetrics();
+ }
+
+ bool processWasLocked = processCount.numLocks > 0;
+
+ processCount.numLocks += aLockAdjust;
+ processCount.numHidden += aHiddenAdjust;
+
+ totalCount.numLocks += aLockAdjust;
+ totalCount.numHidden += aHiddenAdjust;
+
+ if (processCount.numLocks) {
+ table->InsertOrUpdate(aProcessID, processCount);
+ } else {
+ table->Remove(aProcessID);
+ }
+ if (!totalCount.numLocks) {
+ sLockTable->Remove(aTopic);
+ }
+
+ if (sActiveListeners &&
+ (oldState !=
+ ComputeWakeLockState(totalCount.numLocks, totalCount.numHidden) ||
+ processWasLocked != (processCount.numLocks > 0))) {
+ WakeLockInformation info;
+ hal::GetWakeLockInfo(aTopic, &info);
+ NotifyWakeLockChange(info);
+ }
+}
+
+void GetWakeLockInfo(const nsAString& aTopic,
+ WakeLockInformation* aWakeLockInfo) {
+ if (sIsShuttingDown) {
+ NS_WARNING(
+ "You don't want to get wake lock information during xpcom-shutdown!");
+ *aWakeLockInfo = WakeLockInformation();
+ return;
+ }
+
+ if (!sLockTable) {
+ // This can happen during some gtests.
+ NS_WARNING("Attempting to get wake lock information before initialization");
+ *aWakeLockInfo = WakeLockInformation();
+ return;
+ }
+
+ ProcessLockTable* table = sLockTable->Get(aTopic);
+ if (!table) {
+ *aWakeLockInfo = WakeLockInfoFromLockCount(aTopic, LockCount());
+ return;
+ }
+ LockCount totalCount;
+ CountWakeLocks(table, &totalCount);
+ *aWakeLockInfo = WakeLockInfoFromLockCount(aTopic, totalCount);
+}
+
+} // namespace hal_impl
+} // namespace mozilla
diff --git a/hal/HalWakeLock.h b/hal/HalWakeLock.h
new file mode 100644
index 0000000000..8b20f715bd
--- /dev/null
+++ b/hal/HalWakeLock.h
@@ -0,0 +1,26 @@
+/* -*- Mode: C++; tab-width: 40; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/. */
+
+#ifndef __HAL_WAKELOCK_H_
+#define __HAL_WAKELOCK_H_
+
+namespace mozilla {
+namespace hal {
+
+enum WakeLockState {
+ WAKE_LOCK_STATE_UNLOCKED,
+ WAKE_LOCK_STATE_HIDDEN,
+ WAKE_LOCK_STATE_VISIBLE
+};
+
+/**
+ * Return the wake lock state according to the numbers.
+ */
+WakeLockState ComputeWakeLockState(int aNumLocks, int aNumHidden);
+
+} // namespace hal
+} // namespace mozilla
+
+#endif /* __HAL_WAKELOCK_H_ */
diff --git a/hal/HalWakeLockInformation.h b/hal/HalWakeLockInformation.h
new file mode 100644
index 0000000000..2f52b1810e
--- /dev/null
+++ b/hal/HalWakeLockInformation.h
@@ -0,0 +1,18 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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/. */
+#ifndef mozilla_HalWakeLockInformation_h
+#define mozilla_HalWakeLockInformation_h
+
+#include "mozilla/Observer.h"
+
+namespace mozilla {
+namespace hal {
+class WakeLockInformation;
+typedef Observer<hal::WakeLockInformation> WakeLockObserver;
+} // namespace hal
+} // namespace mozilla
+
+#endif // mozilla_HalWakeLockInformation_h
diff --git a/hal/HalWakeLockInternal.h b/hal/HalWakeLockInternal.h
new file mode 100644
index 0000000000..3f99f2a1e1
--- /dev/null
+++ b/hal/HalWakeLockInternal.h
@@ -0,0 +1,17 @@
+/* -*- Mode: C++; tab-width: 40; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/. */
+
+#ifndef __HAL_WAKELOCK_INTERNAL_H_
+#define __HAL_WAKELOCK_INTERNAL_H_
+
+namespace mozilla {
+namespace hal {
+
+void WakeLockInit();
+
+} // namespace hal
+} // namespace mozilla
+
+#endif /* __HAL_WAKELOCK_INTERNAL_H_ */
diff --git a/hal/WindowIdentifier.cpp b/hal/WindowIdentifier.cpp
new file mode 100644
index 0000000000..dc10801691
--- /dev/null
+++ b/hal/WindowIdentifier.cpp
@@ -0,0 +1,61 @@
+/* -*- 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 "WindowIdentifier.h"
+
+#include "mozilla/dom/ContentChild.h"
+#include "nsPIDOMWindow.h"
+
+namespace mozilla {
+namespace hal {
+
+WindowIdentifier::WindowIdentifier()
+ : mWindow(nullptr)
+#ifdef DEBUG
+ ,
+ mIsEmpty(true)
+#endif
+{
+}
+
+WindowIdentifier::WindowIdentifier(nsPIDOMWindowInner* window)
+ : mWindow(window) {
+ mID.AppendElement(GetWindowID());
+}
+
+WindowIdentifier::WindowIdentifier(nsTArray<uint64_t>&& id,
+ nsPIDOMWindowInner* window)
+ : mID(std::move(id)), mWindow(window) {
+ mID.AppendElement(GetWindowID());
+}
+
+const nsTArray<uint64_t>& WindowIdentifier::AsArray() const {
+ MOZ_ASSERT(!mIsEmpty);
+ return mID;
+}
+
+bool WindowIdentifier::HasTraveledThroughIPC() const {
+ MOZ_ASSERT(!mIsEmpty);
+ return mID.Length() >= 2;
+}
+
+void WindowIdentifier::AppendProcessID() {
+ MOZ_ASSERT(!mIsEmpty);
+ mID.AppendElement(dom::ContentChild::GetSingleton()->GetID());
+}
+
+uint64_t WindowIdentifier::GetWindowID() const {
+ MOZ_ASSERT(!mIsEmpty);
+ return mWindow ? mWindow->WindowID() : UINT64_MAX;
+}
+
+nsPIDOMWindowInner* WindowIdentifier::GetWindow() const {
+ MOZ_ASSERT(!mIsEmpty);
+ return mWindow;
+}
+
+} // namespace hal
+} // namespace mozilla
diff --git a/hal/WindowIdentifier.h b/hal/WindowIdentifier.h
new file mode 100644
index 0000000000..11c5140f61
--- /dev/null
+++ b/hal/WindowIdentifier.h
@@ -0,0 +1,106 @@
+/* -*- 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/. */
+
+#ifndef mozilla_hal_WindowIdentifier_h
+#define mozilla_hal_WindowIdentifier_h
+
+#include "mozilla/Types.h"
+#include "nsCOMPtr.h"
+#include "nsTArray.h"
+
+class nsPIDOMWindowInner;
+
+namespace mozilla {
+namespace hal {
+
+/**
+ * This class serves two purposes.
+ *
+ * First, this class wraps a pointer to a window.
+ *
+ * Second, WindowIdentifier lets us uniquely identify a window across
+ * processes. A window exposes an ID which is unique only within its
+ * process. Thus to identify a window, we need to know the ID of the
+ * process which contains it. But the scope of a process's ID is its
+ * parent; that is, two processes with different parents might have
+ * the same ID.
+ *
+ * So to identify a window, we need its ID plus the IDs of all the
+ * processes in the path from the window's process to the root
+ * process. We throw in the IDs of the intermediate windows (a
+ * content window is contained in a window at each level of the
+ * process tree) for good measures.
+ *
+ * You can access this list of IDs by calling AsArray().
+ */
+class WindowIdentifier {
+ public:
+ /**
+ * Create an empty WindowIdentifier. Calls to any of this object's
+ * public methods will assert -- an empty WindowIdentifier may be
+ * used only as a placeholder to code which promises not to touch
+ * the object.
+ */
+ WindowIdentifier();
+
+ /**
+ * Wrap the given window in a WindowIdentifier. These two
+ * constructors automatically grab the window's ID and append it to
+ * the array of IDs.
+ *
+ * Note that these constructors allow an implicit conversion to a
+ * WindowIdentifier.
+ */
+ explicit WindowIdentifier(nsPIDOMWindowInner* window);
+
+ /**
+ * Create a new WindowIdentifier with the given id array and window.
+ * This automatically grabs the window's ID and appends it to the
+ * array.
+ */
+ WindowIdentifier(nsTArray<uint64_t>&& id, nsPIDOMWindowInner* window);
+
+ /**
+ * Get the list of window and process IDs we contain.
+ */
+ typedef nsTArray<uint64_t> IDArrayType;
+ const IDArrayType& AsArray() const;
+
+ /**
+ * Append the ID of the ContentChild singleton to our array of
+ * window/process IDs.
+ */
+ void AppendProcessID();
+
+ /**
+ * Does this WindowIdentifier identify both a window and the process
+ * containing that window? If so, we say it has traveled through
+ * IPC.
+ */
+ bool HasTraveledThroughIPC() const;
+
+ /**
+ * Get the window this object wraps.
+ */
+ nsPIDOMWindowInner* GetWindow() const;
+
+ private:
+ /**
+ * Get the ID of the window object we wrap.
+ */
+ uint64_t GetWindowID() const;
+
+ AutoTArray<uint64_t, 3> mID;
+ nsCOMPtr<nsPIDOMWindowInner> mWindow;
+#ifdef DEBUG
+ bool mIsEmpty = false;
+#endif
+};
+
+} // namespace hal
+} // namespace mozilla
+
+#endif // mozilla_hal_WindowIdentifier_h
diff --git a/hal/android/AndroidHal.cpp b/hal/android/AndroidHal.cpp
new file mode 100644
index 0000000000..3a7e1bf121
--- /dev/null
+++ b/hal/android/AndroidHal.cpp
@@ -0,0 +1,158 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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 "WindowIdentifier.h"
+#include "AndroidBridge.h"
+#include "mozilla/dom/network/Constants.h"
+#include "mozilla/java/GeckoAppShellWrappers.h"
+#include "mozilla/java/GeckoRuntimeWrappers.h"
+#include "mozilla/widget/ScreenManager.h"
+#include "nsPIDOMWindow.h"
+
+using namespace mozilla::dom;
+using namespace mozilla::hal;
+
+namespace mozilla::hal_impl {
+
+void Vibrate(const nsTArray<uint32_t>& pattern, WindowIdentifier&&) {
+ // Ignore the WindowIdentifier parameter; it's here only because hal::Vibrate,
+ // hal_sandbox::Vibrate, and hal_impl::Vibrate all must have the same
+ // signature.
+
+ // Strangely enough, the Android Java API seems to treat vibrate([0]) as a
+ // nop. But we want to treat vibrate([0]) like CancelVibrate! (Note that we
+ // also need to treat vibrate([]) as a call to CancelVibrate.)
+ bool allZero = true;
+ for (uint32_t i = 0; i < pattern.Length(); i++) {
+ if (pattern[i] != 0) {
+ allZero = false;
+ break;
+ }
+ }
+
+ if (allZero) {
+ hal_impl::CancelVibrate(WindowIdentifier());
+ return;
+ }
+
+ AndroidBridge* b = AndroidBridge::Bridge();
+ if (!b) {
+ return;
+ }
+
+ b->Vibrate(pattern);
+}
+
+void CancelVibrate(WindowIdentifier&&) {
+ // Ignore WindowIdentifier parameter.
+
+ java::GeckoAppShell::CancelVibrate();
+}
+
+void EnableBatteryNotifications() {
+ java::GeckoAppShell::EnableBatteryNotifications();
+}
+
+void DisableBatteryNotifications() {
+ java::GeckoAppShell::DisableBatteryNotifications();
+}
+
+void GetCurrentBatteryInformation(hal::BatteryInformation* aBatteryInfo) {
+ AndroidBridge::Bridge()->GetCurrentBatteryInformation(aBatteryInfo);
+}
+
+void EnableNetworkNotifications() {
+ java::GeckoAppShell::EnableNetworkNotifications();
+}
+
+void DisableNetworkNotifications() {
+ java::GeckoAppShell::DisableNetworkNotifications();
+}
+
+void GetCurrentNetworkInformation(hal::NetworkInformation* aNetworkInfo) {
+ AndroidBridge::Bridge()->GetCurrentNetworkInformation(aNetworkInfo);
+}
+
+static bool IsSupportedScreenOrientation(hal::ScreenOrientation aOrientation) {
+ // The Android backend only supports these orientations.
+ static constexpr ScreenOrientation kSupportedOrientations[] = {
+ ScreenOrientation::PortraitPrimary,
+ ScreenOrientation::PortraitSecondary,
+ ScreenOrientation::PortraitPrimary | ScreenOrientation::PortraitSecondary,
+ ScreenOrientation::LandscapePrimary,
+ ScreenOrientation::LandscapeSecondary,
+ ScreenOrientation::LandscapePrimary |
+ ScreenOrientation::LandscapeSecondary,
+ ScreenOrientation::PortraitPrimary |
+ ScreenOrientation::PortraitSecondary |
+ ScreenOrientation::LandscapePrimary |
+ ScreenOrientation::LandscapeSecondary,
+ ScreenOrientation::Default,
+ };
+ for (auto supportedOrientation : kSupportedOrientations) {
+ if (aOrientation == supportedOrientation) {
+ return true;
+ }
+ }
+ return false;
+}
+
+RefPtr<GenericNonExclusivePromise> LockScreenOrientation(
+ const hal::ScreenOrientation& aOrientation) {
+ if (!IsSupportedScreenOrientation(aOrientation)) {
+ NS_WARNING("Unsupported screen orientation type");
+ return GenericNonExclusivePromise::CreateAndReject(
+ NS_ERROR_DOM_NOT_SUPPORTED_ERR, __func__);
+ }
+
+ java::GeckoRuntime::LocalRef runtime = java::GeckoRuntime::GetInstance();
+ if (!runtime) {
+ return GenericNonExclusivePromise::CreateAndReject(NS_ERROR_DOM_ABORT_ERR,
+ __func__);
+ }
+
+ hal::ScreenOrientation orientation = [&aOrientation]() {
+ if (aOrientation == hal::ScreenOrientation::Default) {
+ // GeckoView only supports single monitor, so get primary screen data for
+ // natural orientation.
+ RefPtr<widget::Screen> screen =
+ widget::ScreenManager::GetSingleton().GetPrimaryScreen();
+ return screen->GetDefaultOrientationType();
+ }
+ return aOrientation;
+ }();
+
+ auto result = runtime->LockScreenOrientation(uint32_t(orientation));
+ auto geckoResult = java::GeckoResult::LocalRef(std::move(result));
+ return GenericNonExclusivePromise::FromGeckoResult(geckoResult)
+ ->Then(
+ GetCurrentSerialEventTarget(), __func__,
+ [](const GenericNonExclusivePromise::ResolveOrRejectValue& aValue) {
+ if (aValue.IsResolve()) {
+ if (aValue.ResolveValue()) {
+ return GenericNonExclusivePromise::CreateAndResolve(true,
+ __func__);
+ }
+ // Delegated orientation controller returns failure for
+ // lock.
+ return GenericNonExclusivePromise::CreateAndReject(
+ NS_ERROR_DOM_ABORT_ERR, __func__);
+ }
+ // Browser side doesn't implement orientation delegate.
+ return GenericNonExclusivePromise::CreateAndReject(
+ NS_ERROR_DOM_NOT_SUPPORTED_ERR, __func__);
+ });
+}
+
+void UnlockScreenOrientation() {
+ java::GeckoRuntime::LocalRef runtime = java::GeckoRuntime::GetInstance();
+ if (runtime) {
+ runtime->UnlockScreenOrientation();
+ }
+}
+
+} // namespace mozilla::hal_impl
diff --git a/hal/android/AndroidProcessPriority.cpp b/hal/android/AndroidProcessPriority.cpp
new file mode 100644
index 0000000000..aab2bf50ef
--- /dev/null
+++ b/hal/android/AndroidProcessPriority.cpp
@@ -0,0 +1,62 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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 https://mozilla.org/MPL/2.0/. */
+
+#include "Hal.h"
+
+#include "mozilla/java/GeckoProcessManagerWrappers.h"
+#include "mozilla/java/GeckoProcessTypeWrappers.h"
+#include "mozilla/java/ServiceAllocatorWrappers.h"
+
+using namespace mozilla::hal;
+
+/**
+ * Bucket the Gecko HAL process priority level into one of the three Android
+ * priority levels.
+ */
+static mozilla::java::ServiceAllocator::PriorityLevel::LocalRef
+ToJavaPriorityLevel(const ProcessPriority aPriority) {
+ if (aPriority >= PROCESS_PRIORITY_FOREGROUND) {
+ return mozilla::java::ServiceAllocator::PriorityLevel::FOREGROUND();
+ } else if (aPriority <= PROCESS_PRIORITY_PREALLOC &&
+ aPriority >= PROCESS_PRIORITY_BACKGROUND_PERCEIVABLE) {
+ return mozilla::java::ServiceAllocator::PriorityLevel::BACKGROUND();
+ }
+
+ return mozilla::java::ServiceAllocator::PriorityLevel::IDLE();
+}
+
+namespace mozilla {
+namespace hal_impl {
+
+void SetProcessPriority(int aPid, ProcessPriority aPriority) {
+ if (aPriority == PROCESS_PRIORITY_PARENT_PROCESS) {
+ // This is the parent process itself, which we do not control.
+ return;
+ }
+
+ const int32_t intPriority = static_cast<int32_t>(aPriority);
+ if (intPriority < 0 || intPriority >= NUM_PROCESS_PRIORITY) {
+ return;
+ }
+
+ auto contentProcType = java::GeckoProcessType::CONTENT();
+ auto selector =
+ java::GeckoProcessManager::Selector::New(contentProcType, aPid);
+ auto priorityLevel = ToJavaPriorityLevel(aPriority);
+
+ // To Android, a lower-valued integer is a higher relative priority.
+ // We take the integer value of the enum and subtract it from the value
+ // of the highest Gecko priority level to obtain a 0-based indicator of
+ // the relative priority within the Java PriorityLevel.
+ const int32_t relativeImportance =
+ (static_cast<int32_t>(NUM_PROCESS_PRIORITY) - 1) - intPriority;
+
+ java::GeckoProcessManager::SetProcessPriority(selector, priorityLevel,
+ relativeImportance);
+}
+
+} // namespace hal_impl
+} // namespace mozilla
diff --git a/hal/android/AndroidSensor.cpp b/hal/android/AndroidSensor.cpp
new file mode 100644
index 0000000000..59db64a1d3
--- /dev/null
+++ b/hal/android/AndroidSensor.cpp
@@ -0,0 +1,23 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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 "mozilla/java/GeckoAppShellWrappers.h"
+
+using namespace mozilla::hal;
+
+namespace mozilla {
+namespace hal_impl {
+
+void EnableSensorNotifications(SensorType aSensor) {
+ java::GeckoAppShell::EnableSensor(aSensor);
+}
+
+void DisableSensorNotifications(SensorType aSensor) {
+ java::GeckoAppShell::DisableSensor(aSensor);
+}
+
+} // namespace hal_impl
+} // namespace mozilla
diff --git a/hal/cocoa/CocoaBattery.cpp b/hal/cocoa/CocoaBattery.cpp
new file mode 100644
index 0000000000..a6c4b689e1
--- /dev/null
+++ b/hal/cocoa/CocoaBattery.cpp
@@ -0,0 +1,317 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim set: sw=2 ts=2 et lcs=trail\:.,tab\:>~ : */
+/* 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/. */
+
+#import <CoreFoundation/CoreFoundation.h>
+#import <IOKit/ps/IOPowerSources.h>
+#import <IOKit/ps/IOPSKeys.h>
+
+#include <mozilla/Hal.h>
+#include <mozilla/dom/battery/Constants.h>
+#include <mozilla/Services.h>
+
+#include <nsIObserverService.h>
+#include <nsIObserver.h>
+
+#include <dlfcn.h>
+
+#define IOKIT_FRAMEWORK_PATH "/System/Library/Frameworks/IOKit.framework/IOKit"
+
+#ifndef kIOPSTimeRemainingUnknown
+# define kIOPSTimeRemainingUnknown ((CFTimeInterval)-1.0)
+#endif
+#ifndef kIOPSTimeRemainingUnlimited
+# define kIOPSTimeRemainingUnlimited ((CFTimeInterval)-2.0)
+#endif
+
+using namespace mozilla::dom::battery;
+
+namespace mozilla {
+namespace hal_impl {
+
+typedef CFTimeInterval (*IOPSGetTimeRemainingEstimateFunc)(void);
+
+class MacPowerInformationService {
+ public:
+ static MacPowerInformationService* GetInstance();
+ static void Shutdown();
+ static bool IsShuttingDown();
+
+ void BeginListening();
+ void StopListening();
+
+ static void HandleChange(void* aContext);
+
+ ~MacPowerInformationService();
+
+ private:
+ MacPowerInformationService();
+
+ // The reference to the runloop that is notified of power changes.
+ CFRunLoopSourceRef mRunLoopSource;
+
+ double mLevel;
+ bool mCharging;
+ double mRemainingTime;
+ bool mShouldNotify;
+
+ friend void GetCurrentBatteryInformation(
+ hal::BatteryInformation* aBatteryInfo);
+
+ static MacPowerInformationService* sInstance;
+ static bool sShuttingDown;
+
+ static void* sIOKitFramework;
+ static IOPSGetTimeRemainingEstimateFunc sIOPSGetTimeRemainingEstimate;
+};
+
+void* MacPowerInformationService::sIOKitFramework;
+IOPSGetTimeRemainingEstimateFunc
+ MacPowerInformationService::sIOPSGetTimeRemainingEstimate;
+
+/*
+ * Implementation of mozilla::hal_impl::EnableBatteryNotifications,
+ * mozilla::hal_impl::DisableBatteryNotifications,
+ * and mozilla::hal_impl::GetCurrentBatteryInformation.
+ */
+
+void EnableBatteryNotifications() {
+ if (!MacPowerInformationService::IsShuttingDown()) {
+ MacPowerInformationService::GetInstance()->BeginListening();
+ }
+}
+
+void DisableBatteryNotifications() {
+ if (!MacPowerInformationService::IsShuttingDown()) {
+ MacPowerInformationService::GetInstance()->StopListening();
+ }
+}
+
+void GetCurrentBatteryInformation(hal::BatteryInformation* aBatteryInfo) {
+ MacPowerInformationService* powerService =
+ MacPowerInformationService::GetInstance();
+
+ aBatteryInfo->level() = powerService->mLevel;
+ aBatteryInfo->charging() = powerService->mCharging;
+ aBatteryInfo->remainingTime() = powerService->mRemainingTime;
+}
+
+bool MacPowerInformationService::sShuttingDown = false;
+
+/*
+ * Following is the implementation of MacPowerInformationService.
+ */
+
+MacPowerInformationService* MacPowerInformationService::sInstance = nullptr;
+
+namespace {
+struct SingletonDestroyer final : public nsIObserver {
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIOBSERVER
+
+ private:
+ ~SingletonDestroyer() {}
+};
+
+NS_IMPL_ISUPPORTS(SingletonDestroyer, nsIObserver)
+
+NS_IMETHODIMP
+SingletonDestroyer::Observe(nsISupports*, const char* aTopic, const char16_t*) {
+ MOZ_ASSERT(!strcmp(aTopic, "xpcom-shutdown"));
+ MacPowerInformationService::Shutdown();
+ return NS_OK;
+}
+} // namespace
+
+/* static */
+MacPowerInformationService* MacPowerInformationService::GetInstance() {
+ if (sInstance) {
+ return sInstance;
+ }
+
+ sInstance = new MacPowerInformationService();
+
+ nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
+ if (obs) {
+ obs->AddObserver(new SingletonDestroyer(), "xpcom-shutdown", false);
+ }
+
+ return sInstance;
+}
+
+bool MacPowerInformationService::IsShuttingDown() { return sShuttingDown; }
+
+void MacPowerInformationService::Shutdown() {
+ sShuttingDown = true;
+ delete sInstance;
+ sInstance = nullptr;
+}
+
+MacPowerInformationService::MacPowerInformationService()
+ : mRunLoopSource(nullptr),
+ mLevel(kDefaultLevel),
+ mCharging(kDefaultCharging),
+ mRemainingTime(kDefaultRemainingTime),
+ mShouldNotify(false) {
+ // IOPSGetTimeRemainingEstimate (and the related constants) are only available
+ // on 10.7, so we test for their presence at runtime.
+ sIOKitFramework = dlopen(IOKIT_FRAMEWORK_PATH, RTLD_LAZY | RTLD_LOCAL);
+ if (sIOKitFramework) {
+ sIOPSGetTimeRemainingEstimate = (IOPSGetTimeRemainingEstimateFunc)dlsym(
+ sIOKitFramework, "IOPSGetTimeRemainingEstimate");
+ } else {
+ sIOPSGetTimeRemainingEstimate = nullptr;
+ }
+}
+
+MacPowerInformationService::~MacPowerInformationService() {
+ MOZ_ASSERT(!mRunLoopSource,
+ "The observers have not been correctly removed! "
+ "(StopListening should have been called)");
+
+ if (sIOKitFramework) {
+ dlclose(sIOKitFramework);
+ }
+}
+
+void MacPowerInformationService::BeginListening() {
+ // Set ourselves up to be notified about changes.
+ MOZ_ASSERT(!mRunLoopSource,
+ "IOPS Notification Loop Source already set up. "
+ "(StopListening should have been called)");
+
+ mRunLoopSource = ::IOPSNotificationCreateRunLoopSource(HandleChange, this);
+ if (mRunLoopSource) {
+ ::CFRunLoopAddSource(::CFRunLoopGetCurrent(), mRunLoopSource,
+ kCFRunLoopDefaultMode);
+
+ // Invoke our callback now so we have data if GetCurrentBatteryInformation
+ // is called before a change happens.
+ HandleChange(this);
+ mShouldNotify = true;
+ }
+}
+
+void MacPowerInformationService::StopListening() {
+ if (mRunLoopSource) {
+ ::CFRunLoopRemoveSource(::CFRunLoopGetCurrent(), mRunLoopSource,
+ kCFRunLoopDefaultMode);
+ mRunLoopSource = nullptr;
+ }
+}
+
+void MacPowerInformationService::HandleChange(void* aContext) {
+ MacPowerInformationService* power =
+ static_cast<MacPowerInformationService*>(aContext);
+
+ CFTypeRef data = ::IOPSCopyPowerSourcesInfo();
+ if (!data) {
+ ::CFRelease(data);
+ return;
+ }
+
+ // Get the list of power sources.
+ CFArrayRef list = ::IOPSCopyPowerSourcesList(data);
+ if (!list) {
+ ::CFRelease(list);
+ return;
+ }
+
+ // Default values. These will be used if there are 0 sources or we can't find
+ // better information.
+ double level = kDefaultLevel;
+ double charging = kDefaultCharging;
+ double remainingTime = kDefaultRemainingTime;
+
+ // Look for the first battery power source to give us the information we need.
+ // Usually there's only 1 available, depending on current power source.
+ for (CFIndex i = 0; i < ::CFArrayGetCount(list); ++i) {
+ CFTypeRef source = ::CFArrayGetValueAtIndex(list, i);
+ CFDictionaryRef currPowerSourceDesc =
+ ::IOPSGetPowerSourceDescription(data, source);
+ if (!currPowerSourceDesc) {
+ continue;
+ }
+
+ // Get a battery level estimate. This key is required but does not always
+ // exist.
+ int currentCapacity = 0;
+ const void* cfRef = ::CFDictionaryGetValue(currPowerSourceDesc,
+ CFSTR(kIOPSCurrentCapacityKey));
+ if (!cfRef) {
+ continue;
+ }
+ ::CFNumberGetValue((CFNumberRef)cfRef, kCFNumberSInt32Type,
+ &currentCapacity);
+
+ // This key is also required.
+ int maxCapacity = 0;
+ cfRef =
+ ::CFDictionaryGetValue(currPowerSourceDesc, CFSTR(kIOPSMaxCapacityKey));
+ ::CFNumberGetValue((CFNumberRef)cfRef, kCFNumberSInt32Type, &maxCapacity);
+
+ if (maxCapacity > 0) {
+ level = static_cast<double>(currentCapacity) /
+ static_cast<double>(maxCapacity);
+ }
+
+ // Find out if we're charging.
+ // This key is optional, we fallback to kDefaultCharging if the current
+ // power source doesn't have that info.
+ if (::CFDictionaryGetValueIfPresent(currPowerSourceDesc,
+ CFSTR(kIOPSIsChargingKey), &cfRef)) {
+ charging = ::CFBooleanGetValue((CFBooleanRef)cfRef);
+
+ // Get an estimate of how long it's going to take until we're fully
+ // charged. This key is optional.
+ if (charging) {
+ // Default value that will be changed if we happen to find the actual
+ // remaining time.
+ remainingTime =
+ level == 1.0 ? kDefaultRemainingTime : kUnknownRemainingTime;
+
+ if (::CFDictionaryGetValueIfPresent(
+ currPowerSourceDesc, CFSTR(kIOPSTimeToFullChargeKey), &cfRef)) {
+ int timeToCharge;
+ ::CFNumberGetValue((CFNumberRef)cfRef, kCFNumberIntType,
+ &timeToCharge);
+ if (timeToCharge != kIOPSTimeRemainingUnknown) {
+ remainingTime = timeToCharge * 60;
+ }
+ }
+ } else if (sIOPSGetTimeRemainingEstimate) { // not charging
+ // See if we can get a time estimate.
+ CFTimeInterval estimate = sIOPSGetTimeRemainingEstimate();
+ if (estimate == kIOPSTimeRemainingUnlimited ||
+ estimate == kIOPSTimeRemainingUnknown) {
+ remainingTime = kUnknownRemainingTime;
+ } else {
+ remainingTime = estimate;
+ }
+ }
+ }
+
+ break;
+ }
+
+ bool isNewData = level != power->mLevel || charging != power->mCharging ||
+ remainingTime != power->mRemainingTime;
+
+ power->mRemainingTime = remainingTime;
+ power->mCharging = charging;
+ power->mLevel = level;
+
+ // Notify the observers if stuff changed.
+ if (power->mShouldNotify && isNewData) {
+ hal::NotifyBatteryChange(hal::BatteryInformation(
+ power->mLevel, power->mCharging, power->mRemainingTime));
+ }
+
+ ::CFRelease(data);
+ ::CFRelease(list);
+}
+
+} // namespace hal_impl
+} // namespace mozilla
diff --git a/hal/cocoa/CocoaSensor.mm b/hal/cocoa/CocoaSensor.mm
new file mode 100644
index 0000000000..96e00369e8
--- /dev/null
+++ b/hal/cocoa/CocoaSensor.mm
@@ -0,0 +1,137 @@
+/* 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 "nsITimer.h"
+#include "smslib.h"
+#include "nsComponentManagerUtils.h"
+
+#include <mach/mach.h>
+#include <cmath>
+#import <IOKit/IOKitLib.h>
+
+#define MEAN_GRAVITY 9.80665
+#define DEFAULT_SENSOR_POLL 100
+using namespace mozilla::hal;
+namespace mozilla {
+namespace hal_impl {
+static nsITimer* sUpdateTimer = nullptr;
+static bool sActiveSensors[NUM_SENSOR_TYPE];
+static io_connect_t sDataPort = IO_OBJECT_NULL;
+static uint64_t sLastMean = -1;
+static float LMUvalueToLux(uint64_t aValue) {
+ // Conversion formula from regression. See Bug 793728.
+ // -3*(10^-27)*x^4 + 2.6*(10^-19)*x^3 + -3.4*(10^-12)*x^2 + 3.9*(10^-5)*x - 0.19
+ long double powerC4 = 1 / pow((long double)10, 27);
+ long double powerC3 = 1 / pow((long double)10, 19);
+ long double powerC2 = 1 / pow((long double)10, 12);
+ long double powerC1 = 1 / pow((long double)10, 5);
+
+ long double term4 = -3.0 * powerC4 * pow(aValue, 4);
+ long double term3 = 2.6 * powerC3 * pow(aValue, 3);
+ long double term2 = -3.4 * powerC2 * pow(aValue, 2);
+ long double term1 = 3.9 * powerC1 * aValue;
+
+ float lux = ceil(static_cast<float>(term4 + term3 + term2 + term1 - 0.19));
+ return lux > 0 ? lux : 0;
+}
+void UpdateHandler(nsITimer* aTimer, void* aClosure) {
+ for (int i = 0; i < NUM_SENSOR_TYPE; i++) {
+ if (!sActiveSensors[i]) {
+ continue;
+ }
+ SensorType sensor = static_cast<SensorType>(i);
+ nsTArray<float> values;
+ if (sensor == SENSOR_ACCELERATION) {
+ sms_acceleration accel;
+ smsGetData(&accel);
+
+ values.AppendElement(accel.x * MEAN_GRAVITY);
+ values.AppendElement(accel.y * MEAN_GRAVITY);
+ values.AppendElement(accel.z * MEAN_GRAVITY);
+ } else if (sensor == SENSOR_LIGHT && sDataPort != IO_OBJECT_NULL) {
+ kern_return_t kr;
+ uint32_t outputs = 2;
+ uint64_t lightLMU[outputs];
+
+ kr = IOConnectCallMethod(sDataPort, 0, nil, 0, nil, 0, lightLMU, &outputs, nil, 0);
+ if (kr == KERN_SUCCESS) {
+ uint64_t mean = (lightLMU[0] + lightLMU[1]) / 2;
+ if (mean == sLastMean) {
+ continue;
+ }
+ sLastMean = mean;
+ values.AppendElement(LMUvalueToLux(mean));
+ } else if (kr == kIOReturnBusy) {
+ continue;
+ }
+ }
+
+ hal::SensorData sdata(sensor, PR_Now(), values);
+ hal::NotifySensorChange(sdata);
+ }
+}
+void EnableSensorNotifications(SensorType aSensor) {
+ if (aSensor == SENSOR_ACCELERATION) {
+ int result = smsStartup(nil, nil);
+
+ if (result != SMS_SUCCESS) {
+ return;
+ }
+
+ if (!smsLoadCalibration()) {
+ return;
+ }
+ } else if (aSensor == SENSOR_LIGHT) {
+ io_service_t serviceObject;
+ serviceObject =
+ IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching("AppleLMUController"));
+ if (!serviceObject) {
+ return;
+ }
+ kern_return_t kr;
+ kr = IOServiceOpen(serviceObject, mach_task_self(), 0, &sDataPort);
+ IOObjectRelease(serviceObject);
+ if (kr != KERN_SUCCESS) {
+ return;
+ }
+ } else {
+ NS_WARNING("EnableSensorNotifications called on an unknown sensor type");
+ return;
+ }
+ sActiveSensors[aSensor] = true;
+
+ if (!sUpdateTimer) {
+ CallCreateInstance("@mozilla.org/timer;1", &sUpdateTimer);
+ if (sUpdateTimer) {
+ sUpdateTimer->InitWithNamedFuncCallback(UpdateHandler, nullptr, DEFAULT_SENSOR_POLL,
+ nsITimer::TYPE_REPEATING_SLACK,
+ "hal_impl::UpdateHandler");
+ }
+ }
+}
+void DisableSensorNotifications(SensorType aSensor) {
+ if (!sActiveSensors[aSensor] || (aSensor != SENSOR_ACCELERATION && aSensor != SENSOR_LIGHT)) {
+ return;
+ }
+
+ sActiveSensors[aSensor] = false;
+
+ if (aSensor == SENSOR_ACCELERATION) {
+ smsShutdown();
+ } else if (aSensor == SENSOR_LIGHT) {
+ IOServiceClose(sDataPort);
+ }
+ // If all sensors are disabled, cancel the update timer.
+ if (sUpdateTimer) {
+ for (int i = 0; i < NUM_SENSOR_TYPE; i++) {
+ if (sActiveSensors[i]) {
+ return;
+ }
+ }
+ sUpdateTimer->Cancel();
+ NS_RELEASE(sUpdateTimer);
+ }
+}
+} // namespace hal_impl
+} // namespace mozilla
diff --git a/hal/cocoa/smslib.h b/hal/cocoa/smslib.h
new file mode 100644
index 0000000000..41604cebec
--- /dev/null
+++ b/hal/cocoa/smslib.h
@@ -0,0 +1,158 @@
+/*
+ * smslib.h
+ *
+ * SMSLib Sudden Motion Sensor Access Library
+ * Copyright (c) 2010 Suitable Systems
+ * All rights reserved.
+ *
+ * Developed by: Daniel Griscom
+ * Suitable Systems
+ * http://www.suitable.com
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the
+ * "Software"), to deal with the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * - Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimers.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimers in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * - Neither the names of Suitable Systems nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this Software without specific prior written permission.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE SOFTWARE.
+ *
+ * For more information about SMSLib, see
+ * <http://www.suitable.com/tools/smslib.html>
+ * or contact
+ * Daniel Griscom
+ * Suitable Systems
+ * 1 Centre Street, Suite 204
+ * Wakefield, MA 01880
+ * (781) 665-0053
+ *
+ */
+
+#import <Foundation/Foundation.h>
+
+#define SMSLIB_VERSION "1.8"
+
+#pragma mark Structure definitions
+
+// Structure for specifying a 3-axis acceleration. 0.0 means "zero gravities",
+// 1.0 means "one gravity".
+typedef struct sms_acceleration {
+ float x; // Right-left acceleration (positive is rightwards)
+ float y; // Front-rear acceleration (positive is rearwards)
+ float z; // Up-down acceleration (positive is upwards)
+} sms_acceleration;
+
+// Structure for specifying a calibration.
+typedef struct sms_calibration {
+ float zeros[3]; // Zero points for three axes (X, Y, Z)
+ float onegs[3]; // One gravity values for three axes
+} sms_calibration;
+
+#pragma mark Return value definitions
+
+// These are the return values for accelStartup(), giving the
+// various stages where the most successful attempt at accessing
+// the accelerometer failed. The higher the value, the further along the
+// software progressed before failing. The options are:
+// - Didn't match model name
+#define SMS_FAIL_MODEL (-7)
+// - Failure getting dictionary matching desired services
+#define SMS_FAIL_DICTIONARY (-6)
+// - Failure getting list of services
+#define SMS_FAIL_LIST_SERVICES (-5)
+// - Failure if list of services is empty. The process generally fails
+// here if run on a machine without a Sudden Motion Sensor.
+#define SMS_FAIL_NO_SERVICES (-4)
+// - Failure if error opening device.
+#define SMS_FAIL_OPENING (-3)
+// - Failure if opened, but didn't get a connection
+#define SMS_FAIL_CONNECTION (-2)
+// - Failure if couldn't access connction using given function and size. This
+// is where the process would probably fail with a change in Apple's API.
+// Driver problems often also cause failures here.
+#define SMS_FAIL_ACCESS (-1)
+// - Success!
+#define SMS_SUCCESS (0)
+
+#pragma mark Function declarations
+
+// This starts up the accelerometer code, trying each possible sensor
+// specification. Note that for logging purposes it
+// takes an object and a selector; the object's selector is then invoked
+// with a single NSString as argument giving progress messages. Example
+// logging method:
+// - (void)logMessage: (NSString *)theString
+// which would be used in accelStartup's invocation thusly:
+// result = accelStartup(self, @selector(logMessage:));
+// If the object is nil, then no logging is done. Sets calibation from built-in
+// value table. Returns ACCEL_SUCCESS for success, and other (negative)
+// values for various failures (returns value indicating result of
+// most successful trial).
+int smsStartup(id logObject, SEL logSelector);
+
+// This starts up the library in debug mode, ignoring the actual hardware.
+// Returned data is in the form of 1Hz sine waves, with the X, Y and Z
+// axes 120 degrees out of phase; "calibrated" data has range +/- (1.0/5);
+// "uncalibrated" data has range +/- (256/5). X and Y axes centered on 0.0,
+// Z axes centered on 1 (calibrated) or 256 (uncalibrated).
+// Don't use smsGetBufferLength or smsGetBufferData. Always returns SMS_SUCCESS.
+int smsDebugStartup(id logObject, SEL logSelector);
+
+// Returns the current calibration values.
+void smsGetCalibration(sms_calibration* calibrationRecord);
+
+// Sets the calibration, but does NOT store it as a preference. If the argument
+// is nil then the current calibration is set from the built-in value table.
+void smsSetCalibration(sms_calibration* calibrationRecord);
+
+// Stores the current calibration values as a stored preference.
+void smsStoreCalibration(void);
+
+// Loads the stored preference values into the current calibration.
+// Returns YES if successful.
+BOOL smsLoadCalibration(void);
+
+// Deletes any stored calibration, and then takes the current calibration values
+// from the built-in value table.
+void smsDeleteCalibration(void);
+
+// Fills in the accel record with calibrated acceleration data. Takes
+// 1-2ms to return a value. Returns 0 if success, error number if failure.
+int smsGetData(sms_acceleration* accel);
+
+// Fills in the accel record with uncalibrated acceleration data.
+// Returns 0 if success, error number if failure.
+int smsGetUncalibratedData(sms_acceleration* accel);
+
+// Returns the length of a raw block of data for the current type of sensor.
+int smsGetBufferLength(void);
+
+// Takes a pointer to accelGetRawLength() bytes; sets those bytes
+// to return value from sensor. Make darn sure the buffer length is right!
+void smsGetBufferData(char* buffer);
+
+// This returns an NSString describing the current calibration in
+// human-readable form. Also include a description of the machine.
+NSString* smsGetCalibrationDescription(void);
+
+// Shuts down the accelerometer.
+void smsShutdown(void);
diff --git a/hal/cocoa/smslib.mm b/hal/cocoa/smslib.mm
new file mode 100644
index 0000000000..1a27d404a3
--- /dev/null
+++ b/hal/cocoa/smslib.mm
@@ -0,0 +1,870 @@
+/*
+ * smslib.m
+ *
+ * SMSLib Sudden Motion Sensor Access Library
+ * Copyright (c) 2010 Suitable Systems
+ * All rights reserved.
+ *
+ * Developed by: Daniel Griscom
+ * Suitable Systems
+ * http://www.suitable.com
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the
+ * "Software"), to deal with the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * - Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimers.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimers in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * - Neither the names of Suitable Systems nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this Software without specific prior written permission.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE SOFTWARE.
+ *
+ * For more information about SMSLib, see
+ * <http://www.suitable.com/tools/smslib.html>
+ * or contact
+ * Daniel Griscom
+ * Suitable Systems
+ * 1 Centre Street, Suite 204
+ * Wakefield, MA 01880
+ * (781) 665-0053
+ *
+ */
+
+#import <IOKit/IOKitLib.h>
+#import <sys/sysctl.h>
+#import <math.h>
+#import "smslib.h"
+
+#pragma mark Internal structures
+
+// Represents a single axis of a type of sensor.
+typedef struct axisStruct {
+ int enabled; // Non-zero if axis is valid in this sensor
+ int index; // Location in struct of first byte
+ int size; // Number of bytes
+ float zerog; // Value meaning "zero g"
+ float oneg; // Change in value meaning "increase of one g"
+ // (can be negative if axis sensor reversed)
+} axisStruct;
+
+// Represents the configuration of a type of sensor.
+typedef struct sensorSpec {
+ const char* model; // Prefix of model to be tested
+ const char* name; // Name of device to be read
+ unsigned int function; // Kernel function index
+ int recordSize; // Size of record to be sent/received
+ axisStruct axes[3]; // Description of three axes (X, Y, Z)
+} sensorSpec;
+
+// Configuration of all known types of sensors. The configurations are
+// tried in order until one succeeds in returning data.
+// All default values are set here, but each axis' zerog and oneg values
+// may be changed to saved (calibrated) values.
+//
+// These values came from SeisMaCalibrate calibration reports. In general I've
+// found the following:
+// - All Intel-based SMSs have 250 counts per g, centered on 0, but the signs
+// are different (and in one case two axes are swapped)
+// - PowerBooks and iBooks all have sensors centered on 0, and reading
+// 50-53 steps per gravity (but with differing polarities!)
+// - PowerBooks and iBooks of the same model all have the same axis polarities
+// - PowerBook and iBook access methods are model- and OS version-specific
+//
+// So, the sequence of tests is:
+// - Try model-specific access methods. Note that the test is for a match to the
+// beginning of the model name, e.g. the record with model name "MacBook"
+// matches computer models "MacBookPro1,2" and "MacBook1,1" (and ""
+// matches any model).
+// - If no model-specific record's access fails, then try each model-independent
+// access method in order, stopping when one works.
+static const sensorSpec sensors[] = {
+ // ****** Model-dependent methods ******
+ // The PowerBook5,6 is one of the G4 models that seems to lose
+ // SMS access until the next reboot.
+ {"PowerBook5,6",
+ "IOI2CMotionSensor",
+ 21,
+ 60,
+ {{1, 0, 1, 0, 51.5}, {1, 1, 1, 0, -51.5}, {1, 2, 1, 0, -51.5}}},
+ // The PowerBook5,7 is one of the G4 models that seems to lose
+ // SMS access until the next reboot.
+ {"PowerBook5,7",
+ "IOI2CMotionSensor",
+ 21,
+ 60,
+ {{1, 0, 1, 0, 51.5}, {1, 1, 1, 0, 51.5}, {1, 2, 1, 0, 51.5}}},
+ // Access seems to be reliable on the PowerBook5,8
+ {"PowerBook5,8",
+ "PMUMotionSensor",
+ 21,
+ 60,
+ {{1, 0, 1, 0, -51.5}, {1, 1, 1, 0, 51.5}, {1, 2, 1, 0, -51.5}}},
+ // Access seems to be reliable on the PowerBook5,9
+ {"PowerBook5,9",
+ "PMUMotionSensor",
+ 21,
+ 60,
+ {{1, 0, 1, 0, 51.5}, {1, 1, 1, 0, -51.5}, {1, 2, 1, 0, -51.5}}},
+ // The PowerBook6,7 is one of the G4 models that seems to lose
+ // SMS access until the next reboot.
+ {"PowerBook6,7",
+ "IOI2CMotionSensor",
+ 21,
+ 60,
+ {{1, 0, 1, 0, 51.5}, {1, 1, 1, 0, 51.5}, {1, 2, 1, 0, 51.5}}},
+ // The PowerBook6,8 is one of the G4 models that seems to lose
+ // SMS access until the next reboot.
+ {"PowerBook6,8",
+ "IOI2CMotionSensor",
+ 21,
+ 60,
+ {{1, 0, 1, 0, 51.5}, {1, 1, 1, 0, 51.5}, {1, 2, 1, 0, 51.5}}},
+ // MacBook Pro Core 2 Duo 17". Note the reversed Y and Z axes.
+ {"MacBookPro2,1",
+ "SMCMotionSensor",
+ 5,
+ 40,
+ {{1, 0, 2, 0, 251}, {1, 2, 2, 0, -251}, {1, 4, 2, 0, -251}}},
+ // MacBook Pro Core 2 Duo 15" AND 17" with LED backlight, introduced June '07.
+ // NOTE! The 17" machines have the signs of their X and Y axes reversed
+ // from this calibration, but there's no clear way to discriminate between
+ // the two machines.
+ {"MacBookPro3,1",
+ "SMCMotionSensor",
+ 5,
+ 40,
+ {{1, 0, 2, 0, -251}, {1, 2, 2, 0, 251}, {1, 4, 2, 0, -251}}},
+ // ... specs?
+ {"MacBook5,2",
+ "SMCMotionSensor",
+ 5,
+ 40,
+ {{1, 0, 2, 0, -251}, {1, 2, 2, 0, 251}, {1, 4, 2, 0, -251}}},
+ // ... specs?
+ {"MacBookPro5,1",
+ "SMCMotionSensor",
+ 5,
+ 40,
+ {{1, 0, 2, 0, -251}, {1, 2, 2, 0, -251}, {1, 4, 2, 0, 251}}},
+ // ... specs?
+ {"MacBookPro5,2",
+ "SMCMotionSensor",
+ 5,
+ 40,
+ {{1, 0, 2, 0, -251}, {1, 2, 2, 0, -251}, {1, 4, 2, 0, 251}}},
+ // This is speculative, based on a single user's report. Looks like the X and Y axes
+ // are swapped. This is true for no other known Appple laptop.
+ {"MacBookPro5,3",
+ "SMCMotionSensor",
+ 5,
+ 40,
+ {{1, 2, 2, 0, -251}, {1, 0, 2, 0, -251}, {1, 4, 2, 0, -251}}},
+ // ... specs?
+ {"MacBookPro5,4",
+ "SMCMotionSensor",
+ 5,
+ 40,
+ {{1, 0, 2, 0, -251}, {1, 2, 2, 0, -251}, {1, 4, 2, 0, 251}}},
+ // ****** Model-independent methods ******
+ // Seen once with PowerBook6,8 under system 10.3.9; I suspect
+ // other G4-based 10.3.* systems might use this
+ {"", "IOI2CMotionSensor", 24, 60, {{1, 0, 1, 0, 51.5}, {1, 1, 1, 0, 51.5}, {1, 2, 1, 0, 51.5}}},
+ // PowerBook5,6 , PowerBook5,7 , PowerBook6,7 , PowerBook6,8
+ // under OS X 10.4.*
+ {"", "IOI2CMotionSensor", 21, 60, {{1, 0, 1, 0, 51.5}, {1, 1, 1, 0, 51.5}, {1, 2, 1, 0, 51.5}}},
+ // PowerBook5,8 , PowerBook5,9 under OS X 10.4.*
+ {"",
+ "PMUMotionSensor",
+ 21,
+ 60,
+ {// Each has two out of three gains negative, but it's different
+ // for the different models. So, this will be right in two out
+ // of three axis for either model.
+ {1, 0, 1, 0, -51.5},
+ {1, 1, 1, -6, -51.5},
+ {1, 2, 1, 0, -51.5}}},
+ // All MacBook, MacBookPro models. Hardware (at least on early MacBookPro 15")
+ // is Kionix KXM52-1050 three-axis accelerometer chip. Data is at
+ // http://kionix.com/Product-Index/product-index.htm. Specific MB and MBP models
+ // that use this are:
+ // MacBook1,1
+ // MacBook2,1
+ // MacBook3,1
+ // MacBook4,1
+ // MacBook5,1
+ // MacBook6,1
+ // MacBookAir1,1
+ // MacBookPro1,1
+ // MacBookPro1,2
+ // MacBookPro4,1
+ // MacBookPro5,5
+ {"", "SMCMotionSensor", 5, 40, {{1, 0, 2, 0, 251}, {1, 2, 2, 0, 251}, {1, 4, 2, 0, 251}}}};
+
+#define SENSOR_COUNT (sizeof(sensors) / sizeof(sensorSpec))
+
+#pragma mark Internal prototypes
+
+static int getData(sms_acceleration* accel, int calibrated, id logObject, SEL logSelector);
+static float getAxis(int which, int calibrated);
+static int signExtend(int value, int size);
+static NSString* getModelName(void);
+static NSString* getOSVersion(void);
+static BOOL loadCalibration(void);
+static void storeCalibration(void);
+static void defaultCalibration(void);
+static void deleteCalibration(void);
+static int prefIntRead(NSString* prefName, BOOL* success);
+static void prefIntWrite(NSString* prefName, int prefValue);
+static float prefFloatRead(NSString* prefName, BOOL* success);
+static void prefFloatWrite(NSString* prefName, float prefValue);
+static void prefDelete(NSString* prefName);
+static void prefSynchronize(void);
+// static long getMicroseconds(void);
+float fakeData(NSTimeInterval time);
+
+#pragma mark Static variables
+
+static int debugging = NO; // True if debugging (synthetic data)
+static io_connect_t connection; // Connection for reading accel values
+static int running = NO; // True if we successfully started
+static unsigned int sensorNum = 0; // The current index into sensors[]
+static const char* serviceName; // The name of the current service
+static char *iRecord, *oRecord; // Pointers to read/write records for sensor
+static int recordSize; // Size of read/write records
+static unsigned int function; // Which kernel function should be used
+static float zeros[3]; // X, Y and Z zero calibration values
+static float onegs[3]; // X, Y and Z one-g calibration values
+
+#pragma mark Defines
+
+// Pattern for building axis letter from axis number
+#define INT_TO_AXIS(a) (a == 0 ? @"X" : a == 1 ? @"Y" : @"Z")
+// Name of configuration for given axis' zero (axis specified by integer)
+#define ZERO_NAME(a) [NSString stringWithFormat:@"%@-Axis-Zero", INT_TO_AXIS(a)]
+// Name of configuration for given axis' oneg (axis specified by integer)
+#define ONEG_NAME(a) [NSString stringWithFormat:@"%@-Axis-One-g", INT_TO_AXIS(a)]
+// Name of "Is calibrated" preference
+#define CALIBRATED_NAME (@"Calibrated")
+// Application domain for SeisMac library
+#define APP_ID ((CFStringRef) @"com.suitable.SeisMacLib")
+
+// These #defines make the accelStartup code a LOT easier to read.
+#undef LOG
+#define LOG(message) \
+ if (logObject) { \
+ [logObject performSelector:logSelector withObject:message]; \
+ }
+#define LOG_ARG(format, var1) \
+ if (logObject) { \
+ [logObject performSelector:logSelector withObject:[NSString stringWithFormat:format, var1]]; \
+ }
+#define LOG_2ARG(format, var1, var2) \
+ if (logObject) { \
+ [logObject performSelector:logSelector \
+ withObject:[NSString stringWithFormat:format, var1, var2]]; \
+ }
+#define LOG_3ARG(format, var1, var2, var3) \
+ if (logObject) { \
+ [logObject performSelector:logSelector \
+ withObject:[NSString stringWithFormat:format, var1, var2, var3]]; \
+ }
+
+#pragma mark Function definitions
+
+// This starts up the accelerometer code, trying each possible sensor
+// specification. Note that for logging purposes it
+// takes an object and a selector; the object's selector is then invoked
+// with a single NSString as argument giving progress messages. Example
+// logging method:
+// - (void)logMessage: (NSString *)theString
+// which would be used in accelStartup's invocation thusly:
+// result = accelStartup(self, @selector(logMessage:));
+// If the object is nil, then no logging is done. Sets calibation from built-in
+// value table. Returns ACCEL_SUCCESS for success, and other (negative)
+// values for various failures (returns value indicating result of
+// most successful trial).
+int smsStartup(id logObject, SEL logSelector) {
+ io_iterator_t iterator;
+ io_object_t device;
+ kern_return_t result;
+ sms_acceleration accel;
+ int failure_result = SMS_FAIL_MODEL;
+
+ running = NO;
+ debugging = NO;
+
+ NSString* modelName = getModelName();
+
+ LOG_ARG(@"Machine model: %@\n", modelName);
+ LOG_ARG(@"OS X version: %@\n", getOSVersion());
+ LOG_ARG(@"Accelerometer library version: %s\n", SMSLIB_VERSION);
+
+ for (sensorNum = 0; sensorNum < SENSOR_COUNT; sensorNum++) {
+ // Set up all specs for this type of sensor
+ serviceName = sensors[sensorNum].name;
+ recordSize = sensors[sensorNum].recordSize;
+ function = sensors[sensorNum].function;
+
+ LOG_3ARG(@"Trying service \"%s\" with selector %d and %d byte record:\n", serviceName, function,
+ recordSize);
+
+ NSString* targetName = [NSString stringWithCString:sensors[sensorNum].model
+ encoding:NSMacOSRomanStringEncoding];
+ LOG_ARG(@" Comparing model name to target \"%@\": ", targetName);
+ if ([targetName length] == 0 || [modelName hasPrefix:targetName]) {
+ LOG(@"success.\n");
+ } else {
+ LOG(@"failure.\n");
+ // Don't need to increment failure_result.
+ continue;
+ }
+
+ LOG(@" Fetching dictionary for service: ");
+ CFMutableDictionaryRef dict = IOServiceMatching(serviceName);
+
+ if (dict) {
+ LOG(@"success.\n");
+ } else {
+ LOG(@"failure.\n");
+ if (failure_result < SMS_FAIL_DICTIONARY) {
+ failure_result = SMS_FAIL_DICTIONARY;
+ }
+ continue;
+ }
+
+ LOG(@" Getting list of matching services: ");
+ result = IOServiceGetMatchingServices(kIOMasterPortDefault, dict, &iterator);
+
+ if (result == KERN_SUCCESS) {
+ LOG(@"success.\n");
+ } else {
+ LOG_ARG(@"failure, with return value 0x%x.\n", result);
+ if (failure_result < SMS_FAIL_LIST_SERVICES) {
+ failure_result = SMS_FAIL_LIST_SERVICES;
+ }
+ continue;
+ }
+
+ LOG(@" Getting first device in list: ");
+ device = IOIteratorNext(iterator);
+
+ if (device == 0) {
+ LOG(@"failure.\n");
+ if (failure_result < SMS_FAIL_NO_SERVICES) {
+ failure_result = SMS_FAIL_NO_SERVICES;
+ }
+ continue;
+ } else {
+ LOG(@"success.\n");
+ LOG(@" Opening device: ");
+ }
+
+ result = IOServiceOpen(device, mach_task_self(), 0, &connection);
+
+ if (result != KERN_SUCCESS) {
+ LOG_ARG(@"failure, with return value 0x%x.\n", result);
+ IOObjectRelease(device);
+ if (failure_result < SMS_FAIL_OPENING) {
+ failure_result = SMS_FAIL_OPENING;
+ }
+ continue;
+ } else if (connection == 0) {
+ LOG_ARG(@"'success', but didn't get a connection (return value was: 0x%x).\n", result);
+ IOObjectRelease(device);
+ if (failure_result < SMS_FAIL_CONNECTION) {
+ failure_result = SMS_FAIL_CONNECTION;
+ }
+ continue;
+ } else {
+ IOObjectRelease(device);
+ LOG(@"success.\n");
+ }
+ LOG(@" Testing device.\n");
+
+ defaultCalibration();
+
+ iRecord = (char*)malloc(recordSize);
+ oRecord = (char*)malloc(recordSize);
+
+ running = YES;
+ result = getData(&accel, true, logObject, logSelector);
+ running = NO;
+
+ if (result) {
+ LOG_ARG(@" Failure testing device, with result 0x%x.\n", result);
+ free(iRecord);
+ iRecord = 0;
+ free(oRecord);
+ oRecord = 0;
+ if (failure_result < SMS_FAIL_ACCESS) {
+ failure_result = SMS_FAIL_ACCESS;
+ }
+ continue;
+ } else {
+ LOG(@" Success testing device!\n");
+ running = YES;
+ return SMS_SUCCESS;
+ }
+ }
+ return failure_result;
+}
+
+// This starts up the library in debug mode, ignoring the actual hardware.
+// Returned data is in the form of 1Hz sine waves, with the X, Y and Z
+// axes 120 degrees out of phase; "calibrated" data has range +/- (1.0/5);
+// "uncalibrated" data has range +/- (256/5). X and Y axes centered on 0.0,
+// Z axes centered on 1 (calibrated) or 256 (uncalibrated).
+// Don't use smsGetBufferLength or smsGetBufferData. Always returns SMS_SUCCESS.
+int smsDebugStartup(id logObject, SEL logSelector) {
+ LOG(@"Starting up in debug mode\n");
+ debugging = YES;
+ return SMS_SUCCESS;
+}
+
+// Returns the current calibration values.
+void smsGetCalibration(sms_calibration* calibrationRecord) {
+ int x;
+
+ for (x = 0; x < 3; x++) {
+ calibrationRecord->zeros[x] = (debugging ? 0 : zeros[x]);
+ calibrationRecord->onegs[x] = (debugging ? 256 : onegs[x]);
+ }
+}
+
+// Sets the calibration, but does NOT store it as a preference. If the argument
+// is nil then the current calibration is set from the built-in value table.
+void smsSetCalibration(sms_calibration* calibrationRecord) {
+ int x;
+
+ if (!debugging) {
+ if (calibrationRecord) {
+ for (x = 0; x < 3; x++) {
+ zeros[x] = calibrationRecord->zeros[x];
+ onegs[x] = calibrationRecord->onegs[x];
+ }
+ } else {
+ defaultCalibration();
+ }
+ }
+}
+
+// Stores the current calibration values as a stored preference.
+void smsStoreCalibration(void) {
+ if (!debugging) storeCalibration();
+}
+
+// Loads the stored preference values into the current calibration.
+// Returns YES if successful.
+BOOL smsLoadCalibration(void) {
+ if (debugging) {
+ return YES;
+ } else if (loadCalibration()) {
+ return YES;
+ } else {
+ defaultCalibration();
+ return NO;
+ }
+}
+
+// Deletes any stored calibration, and then takes the current calibration values
+// from the built-in value table.
+void smsDeleteCalibration(void) {
+ if (!debugging) {
+ deleteCalibration();
+ defaultCalibration();
+ }
+}
+
+// Fills in the accel record with calibrated acceleration data. Takes
+// 1-2ms to return a value. Returns 0 if success, error number if failure.
+int smsGetData(sms_acceleration* accel) {
+ NSTimeInterval time;
+ if (debugging) {
+ usleep(1500); // Usually takes 1-2 milliseconds
+ time = [NSDate timeIntervalSinceReferenceDate];
+ accel->x = fakeData(time) / 5;
+ accel->y = fakeData(time - 1) / 5;
+ accel->z = fakeData(time - 2) / 5 + 1.0;
+ return true;
+ } else {
+ return getData(accel, true, nil, nil);
+ }
+}
+
+// Fills in the accel record with uncalibrated acceleration data.
+// Returns 0 if success, error number if failure.
+int smsGetUncalibratedData(sms_acceleration* accel) {
+ NSTimeInterval time;
+ if (debugging) {
+ usleep(1500); // Usually takes 1-2 milliseconds
+ time = [NSDate timeIntervalSinceReferenceDate];
+ accel->x = fakeData(time) * 256 / 5;
+ accel->y = fakeData(time - 1) * 256 / 5;
+ accel->z = fakeData(time - 2) * 256 / 5 + 256;
+ return true;
+ } else {
+ return getData(accel, false, nil, nil);
+ }
+}
+
+// Returns the length of a raw block of data for the current type of sensor.
+int smsGetBufferLength(void) {
+ if (debugging) {
+ return 0;
+ } else if (running) {
+ return sensors[sensorNum].recordSize;
+ } else {
+ return 0;
+ }
+}
+
+// Takes a pointer to accelGetRawLength() bytes; sets those bytes
+// to return value from sensor. Make darn sure the buffer length is right!
+void smsGetBufferData(char* buffer) {
+ IOItemCount iSize = recordSize;
+ IOByteCount oSize = recordSize;
+ kern_return_t result;
+
+ if (debugging || running == NO) {
+ return;
+ }
+
+ memset(iRecord, 1, iSize);
+ memset(buffer, 0, oSize);
+#if __MAC_OS_X_VERSION_MIN_REQUIRED >= 1050
+ const size_t InStructSize = recordSize;
+ size_t OutStructSize = recordSize;
+ result =
+ IOConnectCallStructMethod(connection,
+ function, // magic kernel function number
+ (const void*)iRecord, InStructSize, (void*)buffer, &OutStructSize);
+#else // __MAC_OS_X_VERSION_MIN_REQUIRED 1050
+ result = IOConnectMethodStructureIStructureO(connection,
+ function, // magic kernel function number
+ iSize, &oSize, iRecord, buffer);
+#endif // __MAC_OS_X_VERSION_MIN_REQUIRED 1050
+
+ if (result != KERN_SUCCESS) {
+ running = NO;
+ }
+}
+
+// This returns an NSString describing the current calibration in
+// human-readable form. Also include a description of the machine.
+NSString* smsGetCalibrationDescription(void) {
+ BOOL success;
+ NSMutableString* s = [[NSMutableString alloc] init];
+
+ if (debugging) {
+ [s release];
+ return @"Debugging!";
+ }
+
+ [s appendString:@"---- SeisMac Calibration Record ----\n \n"];
+ [s appendFormat:@"Machine model: %@\n", getModelName()];
+ [s appendFormat:@"OS X build: %@\n", getOSVersion()];
+ [s appendFormat:@"SeisMacLib version %s, record %d\n \n", SMSLIB_VERSION, sensorNum];
+ [s appendFormat:@"Using service \"%s\", function index %d, size %d\n \n", serviceName, function,
+ recordSize];
+ if (prefIntRead(CALIBRATED_NAME, &success) && success) {
+ [s appendString:@"Calibration values (from calibration):\n"];
+ } else {
+ [s appendString:@"Calibration values (from defaults):\n"];
+ }
+ [s appendFormat:@" X-Axis-Zero = %.2f\n", zeros[0]];
+ [s appendFormat:@" X-Axis-One-g = %.2f\n", onegs[0]];
+ [s appendFormat:@" Y-Axis-Zero = %.2f\n", zeros[1]];
+ [s appendFormat:@" Y-Axis-One-g = %.2f\n", onegs[1]];
+ [s appendFormat:@" Z-Axis-Zero = %.2f\n", zeros[2]];
+ [s appendFormat:@" Z-Axis-One-g = %.2f\n \n", onegs[2]];
+ [s appendString:@"---- End Record ----\n"];
+ return s;
+}
+
+// Shuts down the accelerometer.
+void smsShutdown(void) {
+ if (!debugging) {
+ running = NO;
+ if (iRecord) free(iRecord);
+ if (oRecord) free(oRecord);
+ IOServiceClose(connection);
+ }
+}
+
+#pragma mark Internal functions
+
+// Loads the current calibration from the stored preferences.
+// Returns true iff successful.
+BOOL loadCalibration(void) {
+ BOOL thisSuccess, allSuccess;
+ int x;
+
+ prefSynchronize();
+
+ if (prefIntRead(CALIBRATED_NAME, &thisSuccess) && thisSuccess) {
+ // Calibrated. Set all values from saved values.
+ allSuccess = YES;
+ for (x = 0; x < 3; x++) {
+ zeros[x] = prefFloatRead(ZERO_NAME(x), &thisSuccess);
+ allSuccess &= thisSuccess;
+ onegs[x] = prefFloatRead(ONEG_NAME(x), &thisSuccess);
+ allSuccess &= thisSuccess;
+ }
+ return allSuccess;
+ }
+
+ return NO;
+}
+
+// Stores the current calibration into the stored preferences.
+static void storeCalibration(void) {
+ int x;
+ prefIntWrite(CALIBRATED_NAME, 1);
+ for (x = 0; x < 3; x++) {
+ prefFloatWrite(ZERO_NAME(x), zeros[x]);
+ prefFloatWrite(ONEG_NAME(x), onegs[x]);
+ }
+ prefSynchronize();
+}
+
+// Sets the calibration to its default values.
+void defaultCalibration(void) {
+ int x;
+ for (x = 0; x < 3; x++) {
+ zeros[x] = sensors[sensorNum].axes[x].zerog;
+ onegs[x] = sensors[sensorNum].axes[x].oneg;
+ }
+}
+
+// Deletes the stored preferences.
+static void deleteCalibration(void) {
+ int x;
+
+ prefDelete(CALIBRATED_NAME);
+ for (x = 0; x < 3; x++) {
+ prefDelete(ZERO_NAME(x));
+ prefDelete(ONEG_NAME(x));
+ }
+ prefSynchronize();
+}
+
+// Read a named floating point value from the stored preferences. Sets
+// the success boolean based on, you guessed it, whether it succeeds.
+static float prefFloatRead(NSString* prefName, BOOL* success) {
+ float result = 0.0f;
+
+ CFPropertyListRef ref = CFPreferencesCopyAppValue((CFStringRef)prefName, APP_ID);
+ // If there isn't such a preference, fail
+ if (ref == NULL) {
+ *success = NO;
+ return result;
+ }
+ CFTypeID typeID = CFGetTypeID(ref);
+ // Is it a number?
+ if (typeID == CFNumberGetTypeID()) {
+ // Is it a floating point number?
+ if (CFNumberIsFloatType((CFNumberRef)ref)) {
+ // Yup: grab it.
+ *success = CFNumberGetValue((__CFNumber*)ref, kCFNumberFloat32Type, &result);
+ } else {
+ // Nope: grab as an integer, and convert to a float.
+ long num;
+ if (CFNumberGetValue((CFNumberRef)ref, kCFNumberLongType, &num)) {
+ result = num;
+ *success = YES;
+ } else {
+ *success = NO;
+ }
+ }
+ // Or is it a string (e.g. set by the command line "defaults" command)?
+ } else if (typeID == CFStringGetTypeID()) {
+ result = (float)CFStringGetDoubleValue((CFStringRef)ref);
+ *success = YES;
+ } else {
+ // Can't convert to a number: fail.
+ *success = NO;
+ }
+ CFRelease(ref);
+ return result;
+}
+
+// Writes a named floating point value to the stored preferences.
+static void prefFloatWrite(NSString* prefName, float prefValue) {
+ CFNumberRef cfFloat = CFNumberCreate(kCFAllocatorDefault, kCFNumberFloatType, &prefValue);
+ CFPreferencesSetAppValue((CFStringRef)prefName, cfFloat, APP_ID);
+ CFRelease(cfFloat);
+}
+
+// Reads a named integer value from the stored preferences.
+static int prefIntRead(NSString* prefName, BOOL* success) {
+ Boolean internalSuccess;
+ CFIndex result = CFPreferencesGetAppIntegerValue((CFStringRef)prefName, APP_ID, &internalSuccess);
+ *success = internalSuccess;
+
+ return result;
+}
+
+// Writes a named integer value to the stored preferences.
+static void prefIntWrite(NSString* prefName, int prefValue) {
+ CFPreferencesSetAppValue((CFStringRef)prefName, (CFNumberRef)[NSNumber numberWithInt:prefValue],
+ APP_ID);
+}
+
+// Deletes the named preference values.
+static void prefDelete(NSString* prefName) {
+ CFPreferencesSetAppValue((CFStringRef)prefName, NULL, APP_ID);
+}
+
+// Synchronizes the local preferences with the stored preferences.
+static void prefSynchronize(void) { CFPreferencesAppSynchronize(APP_ID); }
+
+// Internal version of accelGetData, with logging
+int getData(sms_acceleration* accel, int calibrated, id logObject, SEL logSelector) {
+ IOItemCount iSize = recordSize;
+ IOByteCount oSize = recordSize;
+ kern_return_t result;
+
+ if (running == NO) {
+ return -1;
+ }
+
+ memset(iRecord, 1, iSize);
+ memset(oRecord, 0, oSize);
+
+ LOG_2ARG(@" Querying device (%u, %d): ", sensors[sensorNum].function,
+ sensors[sensorNum].recordSize);
+
+#if __MAC_OS_X_VERSION_MIN_REQUIRED >= 1050
+ const size_t InStructSize = recordSize;
+ size_t OutStructSize = recordSize;
+ result =
+ IOConnectCallStructMethod(connection,
+ function, // magic kernel function number
+ (const void*)iRecord, InStructSize, (void*)oRecord, &OutStructSize);
+#else // __MAC_OS_X_VERSION_MIN_REQUIRED 1050
+ result = IOConnectMethodStructureIStructureO(connection,
+ function, // magic kernel function number
+ iSize, &oSize, iRecord, oRecord);
+#endif // __MAC_OS_X_VERSION_MIN_REQUIRED 1050
+
+ if (result != KERN_SUCCESS) {
+ LOG(@"failed.\n");
+ running = NO;
+ return result;
+ } else {
+ LOG(@"succeeded.\n");
+
+ accel->x = getAxis(0, calibrated);
+ accel->y = getAxis(1, calibrated);
+ accel->z = getAxis(2, calibrated);
+ return 0;
+ }
+}
+
+// Given the returned record, extracts the value of the given axis. If
+// calibrated, then zero G is 0.0, and one G is 1.0.
+float getAxis(int which, int calibrated) {
+ // Get various values (to make code cleaner)
+ int indx = sensors[sensorNum].axes[which].index;
+ int size = sensors[sensorNum].axes[which].size;
+ float zerog = zeros[which];
+ float oneg = onegs[which];
+ // Storage for value to be returned
+ int value = 0;
+
+ // Although the values in the returned record should have the proper
+ // endianness, we still have to get it into the proper end of value.
+#if (BYTE_ORDER == BIG_ENDIAN)
+ // On PowerPC processors
+ memcpy(((char*)&value) + (sizeof(int) - size), &oRecord[indx], size);
+#endif
+#if (BYTE_ORDER == LITTLE_ENDIAN)
+ // On Intel processors
+ memcpy(&value, &oRecord[indx], size);
+#endif
+
+ value = signExtend(value, size);
+
+ if (calibrated) {
+ // Scale and shift for zero.
+ return ((float)(value - zerog)) / oneg;
+ } else {
+ return value;
+ }
+}
+
+// Extends the sign, given the length of the value.
+int signExtend(int value, int size) {
+ // Extend sign
+ switch (size) {
+ case 1:
+ if (value & 0x00000080) value |= 0xffffff00;
+ break;
+ case 2:
+ if (value & 0x00008000) value |= 0xffff0000;
+ break;
+ case 3:
+ if (value & 0x00800000) value |= 0xff000000;
+ break;
+ }
+ return value;
+}
+
+// Returns the model name of the computer (e.g. "MacBookPro1,1")
+NSString* getModelName(void) {
+ char model[32];
+ size_t len = sizeof(model);
+ int name[2] = {CTL_HW, HW_MODEL};
+ NSString* result;
+
+ if (sysctl(name, 2, &model, &len, NULL, 0) == 0) {
+ result = [NSString stringWithFormat:@"%s", model];
+ } else {
+ result = @"";
+ }
+
+ return result;
+}
+
+// Returns the current OS X version and build (e.g. "10.4.7 (build 8J2135a)")
+NSString* getOSVersion(void) {
+ NSDictionary* dict = [NSDictionary
+ dictionaryWithContentsOfFile:@"/System/Library/CoreServices/SystemVersion.plist"];
+ NSString* versionString = [dict objectForKey:@"ProductVersion"];
+ NSString* buildString = [dict objectForKey:@"ProductBuildVersion"];
+ NSString* wholeString = [NSString stringWithFormat:@"%@ (build %@)", versionString, buildString];
+ return wholeString;
+}
+
+// Returns time within the current second in microseconds.
+// long getMicroseconds() {
+// struct timeval t;
+// gettimeofday(&t, 0);
+// return t.tv_usec;
+//}
+
+// Returns fake data given the time. Range is +/-1.
+float fakeData(NSTimeInterval time) {
+ long secs = lround(floor(time));
+ int secsMod3 = secs % 3;
+ double angle = time * 10 * M_PI * 2;
+ double mag = exp(-(time - (secs - secsMod3)) * 2);
+ return sin(angle) * mag;
+}
diff --git a/hal/fallback/FallbackBattery.cpp b/hal/fallback/FallbackBattery.cpp
new file mode 100644
index 0000000000..b4f2fe105a
--- /dev/null
+++ b/hal/fallback/FallbackBattery.cpp
@@ -0,0 +1,24 @@
+/* -*- 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 "mozilla/dom/battery/Constants.h"
+
+namespace mozilla {
+namespace hal_impl {
+
+void EnableBatteryNotifications() {}
+
+void DisableBatteryNotifications() {}
+
+void GetCurrentBatteryInformation(hal::BatteryInformation* aBatteryInfo) {
+ aBatteryInfo->level() = dom::battery::kDefaultLevel;
+ aBatteryInfo->charging() = dom::battery::kDefaultCharging;
+ aBatteryInfo->remainingTime() = dom::battery::kDefaultRemainingTime;
+}
+
+} // namespace hal_impl
+} // namespace mozilla
diff --git a/hal/fallback/FallbackNetwork.cpp b/hal/fallback/FallbackNetwork.cpp
new file mode 100644
index 0000000000..d944b3a579
--- /dev/null
+++ b/hal/fallback/FallbackNetwork.cpp
@@ -0,0 +1,25 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=8 et :
+ */
+/* 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 "mozilla/dom/network/Constants.h"
+
+namespace mozilla {
+namespace hal_impl {
+
+void EnableNetworkNotifications() {}
+
+void DisableNetworkNotifications() {}
+
+void GetCurrentNetworkInformation(hal::NetworkInformation* aNetworkInfo) {
+ aNetworkInfo->type() = dom::network::kDefaultType;
+ aNetworkInfo->isWifi() = dom::network::kDefaultIsWifi;
+ aNetworkInfo->dhcpGateway() = dom::network::kDefaultDHCPGateway;
+}
+
+} // namespace hal_impl
+} // namespace mozilla
diff --git a/hal/fallback/FallbackProcessPriority.cpp b/hal/fallback/FallbackProcessPriority.cpp
new file mode 100644
index 0000000000..2d747c65e3
--- /dev/null
+++ b/hal/fallback/FallbackProcessPriority.cpp
@@ -0,0 +1,19 @@
+/* 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 "HalLog.h"
+
+using namespace mozilla::hal;
+
+namespace mozilla {
+namespace hal_impl {
+
+void SetProcessPriority(int aPid, ProcessPriority aPriority) {
+ HAL_LOG("FallbackProcessPriority - SetProcessPriority(%d, %s)\n", aPid,
+ ProcessPriorityToString(aPriority));
+}
+
+} // namespace hal_impl
+} // namespace mozilla
diff --git a/hal/fallback/FallbackScreenConfiguration.cpp b/hal/fallback/FallbackScreenConfiguration.cpp
new file mode 100644
index 0000000000..e298855b90
--- /dev/null
+++ b/hal/fallback/FallbackScreenConfiguration.cpp
@@ -0,0 +1,17 @@
+/* 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"
+
+namespace mozilla::hal_impl {
+
+RefPtr<GenericNonExclusivePromise> LockScreenOrientation(
+ const hal::ScreenOrientation& aOrientation) {
+ return GenericNonExclusivePromise::CreateAndReject(
+ NS_ERROR_DOM_NOT_SUPPORTED_ERR, __func__);
+}
+
+void UnlockScreenOrientation() {}
+
+} // namespace mozilla::hal_impl
diff --git a/hal/fallback/FallbackSensor.cpp b/hal/fallback/FallbackSensor.cpp
new file mode 100644
index 0000000000..2ce51904b7
--- /dev/null
+++ b/hal/fallback/FallbackSensor.cpp
@@ -0,0 +1,19 @@
+/* -*- 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 "mozilla/Hal.h"
+
+using namespace mozilla::hal;
+
+namespace mozilla {
+namespace hal_impl {
+
+void EnableSensorNotifications(SensorType aSensor) {}
+
+void DisableSensorNotifications(SensorType aSensor) {}
+
+} // namespace hal_impl
+} // namespace mozilla
diff --git a/hal/fallback/FallbackVibration.cpp b/hal/fallback/FallbackVibration.cpp
new file mode 100644
index 0000000000..f3b0302d03
--- /dev/null
+++ b/hal/fallback/FallbackVibration.cpp
@@ -0,0 +1,19 @@
+/* -*- 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"
+
+using mozilla::hal::WindowIdentifier;
+
+namespace mozilla {
+namespace hal_impl {
+
+void Vibrate(const nsTArray<uint32_t>& pattern, hal::WindowIdentifier&&) {}
+
+void CancelVibrate(hal::WindowIdentifier&&) {}
+
+} // namespace hal_impl
+} // namespace mozilla
diff --git a/hal/linux/LinuxProcessPriority.cpp b/hal/linux/LinuxProcessPriority.cpp
new file mode 100644
index 0000000000..a0f72a4f38
--- /dev/null
+++ b/hal/linux/LinuxProcessPriority.cpp
@@ -0,0 +1,78 @@
+/* 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 "HalLog.h"
+
+#include "mozilla/Sprintf.h"
+#include "mozilla/Unused.h"
+
+#include <fcntl.h>
+#include <unistd.h>
+
+using namespace mozilla::hal;
+
+namespace mozilla::hal_impl {
+
+/* The Linux OOM score is a value computed by the kernel ranging between 0 and
+ * 1000 and indicating how likely is a process to be killed when memory is
+ * tight. The larger the value the more likely a process is to be killed. The
+ * score is computed based on the amount of memory used plus an adjustment
+ * value. We chose our adjustment values to make it likely that processes are
+ * killed in the following order:
+ *
+ * 1. preallocated processes
+ * 2. background processes
+ * 3. background processes playing video (but no audio)
+ * 4. foreground processes or processes playing or recording audio
+ * 5. the parent process
+ *
+ * At the time of writing (2022) the base score for a process consuming very
+ * little memory seems to be around ~667. Our adjustments are thus designed
+ * to ensure this order starting from a 667 baseline but trying not to go too
+ * close to the 1000 limit where they would be clamped. */
+
+const uint32_t kParentOomScoreAdjust = 0;
+const uint32_t kForegroundOomScoreAdjust = 100;
+const uint32_t kBackgroundPerceivableOomScoreAdjust = 133;
+const uint32_t kBackgroundOomScoreAdjust = 167;
+const uint32_t kPreallocOomScoreAdjust = 233;
+
+static uint32_t OomScoreAdjForPriority(ProcessPriority aPriority) {
+ switch (aPriority) {
+ case PROCESS_PRIORITY_BACKGROUND:
+ return kBackgroundOomScoreAdjust;
+ case PROCESS_PRIORITY_BACKGROUND_PERCEIVABLE:
+ return kBackgroundPerceivableOomScoreAdjust;
+ case PROCESS_PRIORITY_PREALLOC:
+ return kPreallocOomScoreAdjust;
+ case PROCESS_PRIORITY_FOREGROUND:
+ return kForegroundOomScoreAdjust;
+ default:
+ return kParentOomScoreAdjust;
+ }
+}
+
+void SetProcessPriority(int aPid, ProcessPriority aPriority) {
+ HAL_LOG("LinuxProcessPriority - SetProcessPriority(%d, %s)\n", aPid,
+ ProcessPriorityToString(aPriority));
+
+ uint32_t oomScoreAdj = OomScoreAdjForPriority(aPriority);
+
+ char path[32] = {};
+ SprintfLiteral(path, "/proc/%d/oom_score_adj", aPid);
+
+ char oomScoreAdjStr[11] = {};
+ SprintfLiteral(oomScoreAdjStr, "%d", oomScoreAdj);
+
+ int fd = open(path, O_WRONLY);
+ if (fd < 0) {
+ return;
+ }
+ const size_t len = strlen(oomScoreAdjStr);
+ Unused << write(fd, oomScoreAdjStr, len);
+ Unused << close(fd);
+}
+
+} // namespace mozilla::hal_impl
diff --git a/hal/linux/UPowerClient.cpp b/hal/linux/UPowerClient.cpp
new file mode 100644
index 0000000000..1de38ad201
--- /dev/null
+++ b/hal/linux/UPowerClient.cpp
@@ -0,0 +1,448 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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 "HalLog.h"
+#include <dbus/dbus-glib.h>
+#include <dbus/dbus-glib-lowlevel.h>
+#include <mozilla/Attributes.h>
+#include <mozilla/dom/battery/Constants.h>
+#include "mozilla/GRefPtr.h"
+#include "mozilla/GUniquePtr.h"
+#include <cmath>
+
+using namespace mozilla::dom::battery;
+
+namespace mozilla::hal_impl {
+
+/**
+ * This is the declaration of UPowerClient class. This class is listening and
+ * communicating to upower daemon through DBus.
+ * There is no header file because this class shouldn't be public.
+ */
+class UPowerClient {
+ public:
+ static UPowerClient* GetInstance();
+
+ void BeginListening();
+ void StopListening();
+
+ double GetLevel();
+ bool IsCharging();
+ double GetRemainingTime();
+
+ ~UPowerClient();
+
+ private:
+ UPowerClient();
+
+ enum States {
+ eState_Unknown = 0,
+ eState_Charging,
+ eState_Discharging,
+ eState_Empty,
+ eState_FullyCharged,
+ eState_PendingCharge,
+ eState_PendingDischarge
+ };
+
+ /**
+ * Update the currently tracked device.
+ * @return whether everything went ok.
+ */
+ void UpdateTrackedDeviceSync();
+
+ /**
+ * Returns a hash table with the properties of aDevice.
+ */
+ already_AddRefed<GHashTable> GetDevicePropertiesSync(DBusGProxy* aProxy);
+ void GetDevicePropertiesAsync(DBusGProxy* aProxy);
+ static void GetDevicePropertiesCallback(DBusGProxy* aProxy,
+ DBusGProxyCall* aCall, void* aData);
+
+ /**
+ * Using the device properties (aHashTable), this method updates the member
+ * variable storing the values we care about.
+ */
+ void UpdateSavedInfo(GHashTable* aHashTable);
+
+ /**
+ * Callback used by 'DeviceChanged' signal.
+ */
+ static void DeviceChanged(DBusGProxy* aProxy, const gchar* aObjectPath,
+ UPowerClient* aListener);
+
+ /**
+ * Callback used by 'PropertiesChanged' signal.
+ * This method is called when the the battery level changes.
+ * (Only with upower >= 0.99)
+ */
+ static void PropertiesChanged(DBusGProxy* aProxy, const gchar*, GHashTable*,
+ char**, UPowerClient* aListener);
+
+ /**
+ * Callback called when mDBusConnection gets a signal.
+ */
+ static DBusHandlerResult ConnectionSignalFilter(DBusConnection* aConnection,
+ DBusMessage* aMessage,
+ void* aData);
+
+ // The DBus connection object.
+ RefPtr<DBusGConnection> mDBusConnection;
+
+ // The DBus proxy object to upower.
+ RefPtr<DBusGProxy> mUPowerProxy;
+
+ // The path of the tracked device.
+ GUniquePtr<gchar> mTrackedDevice;
+
+ // The DBusGProxy for the tracked device.
+ RefPtr<DBusGProxy> mTrackedDeviceProxy;
+
+ double mLevel;
+ bool mCharging;
+ double mRemainingTime;
+
+ static UPowerClient* sInstance;
+
+ static const guint sDeviceTypeBattery = 2;
+ static const guint64 kUPowerUnknownRemainingTime = 0;
+};
+
+/*
+ * Implementation of mozilla::hal_impl::EnableBatteryNotifications,
+ * mozilla::hal_impl::DisableBatteryNotifications,
+ * and mozilla::hal_impl::GetCurrentBatteryInformation.
+ */
+
+void EnableBatteryNotifications() {
+ UPowerClient::GetInstance()->BeginListening();
+}
+
+void DisableBatteryNotifications() {
+ UPowerClient::GetInstance()->StopListening();
+}
+
+void GetCurrentBatteryInformation(hal::BatteryInformation* aBatteryInfo) {
+ UPowerClient* upowerClient = UPowerClient::GetInstance();
+
+ aBatteryInfo->level() = upowerClient->GetLevel();
+ aBatteryInfo->charging() = upowerClient->IsCharging();
+ aBatteryInfo->remainingTime() = upowerClient->GetRemainingTime();
+}
+
+/*
+ * Following is the implementation of UPowerClient.
+ */
+
+UPowerClient* UPowerClient::sInstance = nullptr;
+
+/* static */
+UPowerClient* UPowerClient::GetInstance() {
+ if (!sInstance) {
+ sInstance = new UPowerClient();
+ }
+
+ return sInstance;
+}
+
+UPowerClient::UPowerClient()
+ : mLevel(kDefaultLevel),
+ mCharging(kDefaultCharging),
+ mRemainingTime(kDefaultRemainingTime) {}
+
+UPowerClient::~UPowerClient() {
+ NS_ASSERTION(!mDBusConnection && !mUPowerProxy && !mTrackedDevice &&
+ !mTrackedDeviceProxy,
+ "The observers have not been correctly removed! "
+ "(StopListening should have been called)");
+}
+
+void UPowerClient::BeginListening() {
+ GUniquePtr<GError> error;
+ mDBusConnection =
+ dont_AddRef(dbus_g_bus_get(DBUS_BUS_SYSTEM, getter_Transfers(error)));
+
+ if (!mDBusConnection) {
+ HAL_LOG("Failed to open connection to bus: %s\n", error->message);
+ return;
+ }
+
+ DBusConnection* dbusConnection =
+ dbus_g_connection_get_connection(mDBusConnection);
+
+ // Make sure we do not exit the entire program if DBus connection get lost.
+ dbus_connection_set_exit_on_disconnect(dbusConnection, false);
+
+ // Listening to signals the DBus connection is going to get so we will know
+ // when it is lost and we will be able to disconnect cleanly.
+ dbus_connection_add_filter(dbusConnection, ConnectionSignalFilter, this,
+ nullptr);
+
+ mUPowerProxy = dont_AddRef(dbus_g_proxy_new_for_name(
+ mDBusConnection, "org.freedesktop.UPower", "/org/freedesktop/UPower",
+ "org.freedesktop.UPower"));
+
+ UpdateTrackedDeviceSync();
+
+ /*
+ * TODO: we should probably listen to DeviceAdded and DeviceRemoved signals.
+ * If we do that, we would have to disconnect from those in StopListening.
+ * It's not yet implemented because it requires testing hot plugging and
+ * removal of a battery.
+ */
+ dbus_g_proxy_add_signal(mUPowerProxy, "DeviceChanged", G_TYPE_STRING,
+ G_TYPE_INVALID);
+ dbus_g_proxy_connect_signal(mUPowerProxy, "DeviceChanged",
+ G_CALLBACK(DeviceChanged), this, nullptr);
+}
+
+void UPowerClient::StopListening() {
+ // If mDBusConnection isn't initialized, that means we are not really
+ // listening.
+ if (!mDBusConnection) {
+ return;
+ }
+
+ dbus_connection_remove_filter(
+ dbus_g_connection_get_connection(mDBusConnection), ConnectionSignalFilter,
+ this);
+
+ dbus_g_proxy_disconnect_signal(mUPowerProxy, "DeviceChanged",
+ G_CALLBACK(DeviceChanged), this);
+
+ mTrackedDevice = nullptr;
+
+ if (mTrackedDeviceProxy) {
+ dbus_g_proxy_disconnect_signal(mTrackedDeviceProxy, "PropertiesChanged",
+ G_CALLBACK(PropertiesChanged), this);
+ mTrackedDeviceProxy = nullptr;
+ }
+
+ mUPowerProxy = nullptr;
+ mDBusConnection = nullptr;
+
+ // We should now show the default values, not the latest we got.
+ mLevel = kDefaultLevel;
+ mCharging = kDefaultCharging;
+ mRemainingTime = kDefaultRemainingTime;
+}
+
+void UPowerClient::UpdateTrackedDeviceSync() {
+ GType typeGPtrArray =
+ dbus_g_type_get_collection("GPtrArray", DBUS_TYPE_G_OBJECT_PATH);
+ GPtrArray* devices = nullptr;
+
+ // Reset the current tracked device:
+ mTrackedDevice = nullptr;
+
+ // Reset the current tracked device proxy:
+ if (mTrackedDeviceProxy) {
+ dbus_g_proxy_disconnect_signal(mTrackedDeviceProxy, "PropertiesChanged",
+ G_CALLBACK(PropertiesChanged), this);
+ mTrackedDeviceProxy = nullptr;
+ }
+
+ GUniquePtr<GError> error;
+ // If that fails, that likely means upower isn't installed.
+ if (!dbus_g_proxy_call(mUPowerProxy, "EnumerateDevices",
+ getter_Transfers(error), G_TYPE_INVALID, typeGPtrArray,
+ &devices, G_TYPE_INVALID)) {
+ HAL_LOG("Error: %s\n", error->message);
+ return;
+ }
+
+ /*
+ * We are looking for the first device that is a battery.
+ * TODO: we could try to combine more than one battery.
+ */
+ for (guint i = 0; i < devices->len; ++i) {
+ GUniquePtr<gchar> devicePath(
+ static_cast<gchar*>(g_ptr_array_index(devices, i)));
+ if (mTrackedDevice) {
+ continue;
+ }
+
+ RefPtr<DBusGProxy> proxy = dont_AddRef(dbus_g_proxy_new_from_proxy(
+ mUPowerProxy, "org.freedesktop.DBus.Properties", devicePath.get()));
+
+ RefPtr<GHashTable> hashTable(GetDevicePropertiesSync(proxy));
+
+ if (g_value_get_uint(static_cast<const GValue*>(
+ g_hash_table_lookup(hashTable, "Type"))) == sDeviceTypeBattery) {
+ UpdateSavedInfo(hashTable);
+ mTrackedDevice = std::move(devicePath);
+ mTrackedDeviceProxy = std::move(proxy);
+ // Can't break here because we still need to iterate over all other
+ // devices to free them.
+ }
+ }
+
+ if (mTrackedDeviceProxy) {
+ dbus_g_proxy_add_signal(
+ mTrackedDeviceProxy, "PropertiesChanged", G_TYPE_STRING,
+ dbus_g_type_get_map("GHashTable", G_TYPE_STRING, G_TYPE_VALUE),
+ G_TYPE_STRV, G_TYPE_INVALID);
+ dbus_g_proxy_connect_signal(mTrackedDeviceProxy, "PropertiesChanged",
+ G_CALLBACK(PropertiesChanged), this, nullptr);
+ }
+
+ g_ptr_array_free(devices, true);
+}
+
+/* static */
+void UPowerClient::DeviceChanged(DBusGProxy* aProxy, const gchar* aObjectPath,
+ UPowerClient* aListener) {
+ if (!aListener->mTrackedDevice) {
+ return;
+ }
+
+#if GLIB_MAJOR_VERSION >= 2 && GLIB_MINOR_VERSION >= 16
+ if (g_strcmp0(aObjectPath, aListener->mTrackedDevice.get())) {
+#else
+ if (g_ascii_strcasecmp(aObjectPath, aListener->mTrackedDevice.get())) {
+#endif
+ return;
+ }
+
+ aListener->GetDevicePropertiesAsync(aListener->mTrackedDeviceProxy);
+}
+
+/* static */
+void UPowerClient::PropertiesChanged(DBusGProxy* aProxy, const gchar*,
+ GHashTable*, char**,
+ UPowerClient* aListener) {
+ aListener->GetDevicePropertiesAsync(aListener->mTrackedDeviceProxy);
+}
+
+/* static */
+DBusHandlerResult UPowerClient::ConnectionSignalFilter(
+ DBusConnection* aConnection, DBusMessage* aMessage, void* aData) {
+ if (dbus_message_is_signal(aMessage, DBUS_INTERFACE_LOCAL, "Disconnected")) {
+ static_cast<UPowerClient*>(aData)->StopListening();
+ // We do not return DBUS_HANDLER_RESULT_HANDLED here because the connection
+ // might be shared and some other filters might want to do something.
+ }
+
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+already_AddRefed<GHashTable> UPowerClient::GetDevicePropertiesSync(
+ DBusGProxy* aProxy) {
+ GUniquePtr<GError> error;
+ RefPtr<GHashTable> hashTable;
+ GType typeGHashTable =
+ dbus_g_type_get_map("GHashTable", G_TYPE_STRING, G_TYPE_VALUE);
+ if (!dbus_g_proxy_call(aProxy, "GetAll", getter_Transfers(error),
+ G_TYPE_STRING, "org.freedesktop.UPower.Device",
+ G_TYPE_INVALID, typeGHashTable,
+ hashTable.StartAssignment(), G_TYPE_INVALID)) {
+ HAL_LOG("Error: %s\n", error->message);
+ return nullptr;
+ }
+
+ return hashTable.forget();
+}
+
+/* static */
+void UPowerClient::GetDevicePropertiesCallback(DBusGProxy* aProxy,
+ DBusGProxyCall* aCall,
+ void* aData) {
+ GUniquePtr<GError> error;
+ RefPtr<GHashTable> hashTable;
+ GType typeGHashTable =
+ dbus_g_type_get_map("GHashTable", G_TYPE_STRING, G_TYPE_VALUE);
+ if (!dbus_g_proxy_end_call(aProxy, aCall, getter_Transfers(error),
+ typeGHashTable, hashTable.StartAssignment(),
+ G_TYPE_INVALID)) {
+ HAL_LOG("Error: %s\n", error->message);
+ } else {
+ sInstance->UpdateSavedInfo(hashTable);
+ hal::NotifyBatteryChange(hal::BatteryInformation(
+ sInstance->mLevel, sInstance->mCharging, sInstance->mRemainingTime));
+ g_hash_table_unref(hashTable);
+ }
+}
+
+void UPowerClient::GetDevicePropertiesAsync(DBusGProxy* aProxy) {
+ dbus_g_proxy_begin_call(aProxy, "GetAll", GetDevicePropertiesCallback,
+ nullptr, nullptr, G_TYPE_STRING,
+ "org.freedesktop.UPower.Device", G_TYPE_INVALID);
+}
+
+void UPowerClient::UpdateSavedInfo(GHashTable* aHashTable) {
+ bool isFull = false;
+
+ /*
+ * State values are confusing...
+ * First of all, after looking at upower sources (0.9.13), it seems that
+ * PendingDischarge and PendingCharge are not used.
+ * In addition, FullyCharged and Empty states are not clear because we do not
+ * know if the battery is actually charging or not. Those values come directly
+ * from sysfs (in the Linux kernel) which have four states: "Empty", "Full",
+ * "Charging" and "Discharging". In sysfs, "Empty" and "Full" are also only
+ * related to the level, not to the charging state.
+ * In this code, we are going to assume that Full means charging and Empty
+ * means discharging because if that is not the case, the state should not
+ * last a long time (actually, it should disappear at the following update).
+ * It might be even very hard to see real cases where the state is Empty and
+ * the battery is charging or the state is Full and the battery is discharging
+ * given that plugging/unplugging the battery should have an impact on the
+ * level.
+ */
+ switch (g_value_get_uint(
+ static_cast<const GValue*>(g_hash_table_lookup(aHashTable, "State")))) {
+ case eState_Unknown:
+ mCharging = kDefaultCharging;
+ break;
+ case eState_FullyCharged:
+ isFull = true;
+ [[fallthrough]];
+ case eState_Charging:
+ case eState_PendingCharge:
+ mCharging = true;
+ break;
+ case eState_Discharging:
+ case eState_Empty:
+ case eState_PendingDischarge:
+ mCharging = false;
+ break;
+ }
+
+ /*
+ * The battery level might be very close to 100% (like 99%) without
+ * increasing. It seems that upower sets the battery state as 'full' in that
+ * case so we should trust it and not even try to get the value.
+ */
+ if (isFull) {
+ mLevel = 1.0;
+ } else {
+ mLevel = round(g_value_get_double(static_cast<const GValue*>(
+ g_hash_table_lookup(aHashTable, "Percentage")))) *
+ 0.01;
+ }
+
+ if (isFull) {
+ mRemainingTime = 0;
+ } else {
+ mRemainingTime = mCharging
+ ? g_value_get_int64(static_cast<const GValue*>(
+ g_hash_table_lookup(aHashTable, "TimeToFull")))
+ : g_value_get_int64(static_cast<const GValue*>(
+ g_hash_table_lookup(aHashTable, "TimeToEmpty")));
+
+ if (mRemainingTime == kUPowerUnknownRemainingTime) {
+ mRemainingTime = kUnknownRemainingTime;
+ }
+ }
+}
+
+double UPowerClient::GetLevel() { return mLevel; }
+
+bool UPowerClient::IsCharging() { return mCharging; }
+
+double UPowerClient::GetRemainingTime() { return mRemainingTime; }
+
+} // namespace mozilla::hal_impl
diff --git a/hal/moz.build b/hal/moz.build
new file mode 100644
index 0000000000..f5534acb1a
--- /dev/null
+++ b/hal/moz.build
@@ -0,0 +1,136 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+with Files("**"):
+ BUG_COMPONENT = ("Core", "Hardware Abstraction Layer (HAL)")
+
+EXPORTS.mozilla += [
+ "Hal.h",
+ "HalBatteryInformation.h",
+ "HalImpl.h",
+ "HalIPCUtils.h",
+ "HalNetworkInformation.h",
+ "HalSandbox.h",
+ "HalScreenConfiguration.h",
+ "HalSensor.h",
+ "HalTypes.h",
+ "HalWakeLock.h",
+ "HalWakeLockInformation.h",
+]
+
+UNIFIED_SOURCES += [
+ "HalWakeLock.cpp",
+ "sandbox/SandboxHal.cpp",
+ "WindowIdentifier.cpp",
+]
+
+# Hal.cpp cannot be built in unified mode because it relies on HalImpl.h.
+SOURCES += [
+ "Hal.cpp",
+]
+
+if CONFIG["MOZ_WIDGET_TOOLKIT"] == "android":
+ LOCAL_INCLUDES += [
+ "/widget/android",
+ ]
+ UNIFIED_SOURCES += [
+ "android/AndroidProcessPriority.cpp",
+ "android/AndroidSensor.cpp",
+ ]
+ # AndroidHal.cpp cannot be built in unified mode because it relies on HalImpl.h.
+ SOURCES += [
+ "android/AndroidHal.cpp",
+ ]
+elif CONFIG["OS_TARGET"] == "Linux":
+
+ UNIFIED_SOURCES += [
+ "fallback/FallbackScreenConfiguration.cpp",
+ "fallback/FallbackSensor.cpp",
+ "fallback/FallbackVibration.cpp",
+ "linux/LinuxProcessPriority.cpp",
+ ]
+ if CONFIG["MOZ_ENABLE_DBUS"]:
+ UNIFIED_SOURCES += [
+ "linux/UPowerClient.cpp",
+ ]
+ else:
+ UNIFIED_SOURCES += [
+ "fallback/FallbackBattery.cpp",
+ ]
+elif CONFIG["OS_TARGET"] == "WINNT":
+ UNIFIED_SOURCES += [
+ "fallback/FallbackVibration.cpp",
+ "windows/WindowsSensor.cpp",
+ ]
+ # WindowsBattery.cpp cannot be built in unified mode because it relies on HalImpl.h.
+ # WindowsProcessPriority.cpp needs to set _WIN32_WINNT to Windows 8
+ # WindowsScreenConfiguration.cpp needs to set WINVER to 0x0602.
+ SOURCES += [
+ "windows/WindowsBattery.cpp",
+ "windows/WindowsProcessPriority.cpp",
+ "windows/WindowsScreenConfiguration.cpp",
+ ]
+elif CONFIG["MOZ_WIDGET_TOOLKIT"] == "cocoa":
+ UNIFIED_SOURCES += [
+ "cocoa/CocoaBattery.cpp",
+ "fallback/FallbackProcessPriority.cpp",
+ "fallback/FallbackScreenConfiguration.cpp",
+ "fallback/FallbackVibration.cpp",
+ ]
+elif CONFIG["OS_TARGET"] in ("OpenBSD", "NetBSD", "FreeBSD", "DragonFly"):
+ UNIFIED_SOURCES += [
+ "fallback/FallbackProcessPriority.cpp",
+ "fallback/FallbackScreenConfiguration.cpp",
+ "fallback/FallbackSensor.cpp",
+ "fallback/FallbackVibration.cpp",
+ ]
+ if CONFIG["MOZ_ENABLE_DBUS"]:
+ UNIFIED_SOURCES += [
+ "linux/UPowerClient.cpp",
+ ]
+ else:
+ UNIFIED_SOURCES += [
+ "fallback/FallbackBattery.cpp",
+ ]
+else:
+ UNIFIED_SOURCES += [
+ "fallback/FallbackBattery.cpp",
+ "fallback/FallbackProcessPriority.cpp",
+ "fallback/FallbackScreenConfiguration.cpp",
+ "fallback/FallbackSensor.cpp",
+ "fallback/FallbackVibration.cpp",
+ ]
+
+# Fallbacks for backends implemented on Android only.
+if CONFIG["MOZ_WIDGET_TOOLKIT"] != "android":
+ UNIFIED_SOURCES += [
+ "fallback/FallbackNetwork.cpp",
+ ]
+
+if CONFIG["MOZ_WIDGET_TOOLKIT"] == "cocoa":
+ UNIFIED_SOURCES += [
+ "cocoa/CocoaSensor.mm",
+ "cocoa/smslib.mm",
+ ]
+
+IPDL_SOURCES = [
+ "sandbox/PHal.ipdl",
+]
+
+include("/ipc/chromium/chromium-config.mozbuild")
+
+FINAL_LIBRARY = "xul"
+
+LOCAL_INCLUDES += [
+ "/dom/base",
+]
+
+CFLAGS += CONFIG["GLIB_CFLAGS"]
+CFLAGS += CONFIG["MOZ_GTK3_CFLAGS"]
+CFLAGS += CONFIG["MOZ_DBUS_GLIB_CFLAGS"]
+CXXFLAGS += CONFIG["GLIB_CFLAGS"]
+CXXFLAGS += CONFIG["MOZ_GTK3_CFLAGS"]
+CXXFLAGS += CONFIG["MOZ_DBUS_GLIB_CFLAGS"]
diff --git a/hal/sandbox/PHal.ipdl b/hal/sandbox/PHal.ipdl
new file mode 100644
index 0000000000..f47df37f84
--- /dev/null
+++ b/hal/sandbox/PHal.ipdl
@@ -0,0 +1,99 @@
+/* -*- 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 protocol PContent;
+include protocol PBrowser;
+
+include "mozilla/dom/ReferrerInfoUtils.h";
+include "mozilla/GfxMessageUtils.h";
+
+using mozilla::hal::ScreenOrientation from "mozilla/HalIPCUtils.h";
+using mozilla::hal::SensorType from "mozilla/HalSensor.h";
+using mozilla::hal::WakeLockControl from "mozilla/HalTypes.h";
+using mozilla::hal::ProcessPriority from "mozilla/HalTypes.h";
+using nsIntRect from "nsRect.h";
+using PRTime from "prtime.h";
+
+namespace mozilla {
+
+namespace hal {
+struct BatteryInformation {
+ double level;
+ bool charging;
+ double remainingTime;
+};
+
+struct SensorData {
+ SensorType sensor;
+ PRTime timestamp;
+ float[] values;
+};
+
+struct NetworkInformation {
+ uint32_t type;
+ bool isWifi;
+ uint32_t dhcpGateway;
+};
+
+struct WakeLockInformation {
+ nsString topic;
+ uint32_t numLocks;
+ uint32_t numHidden;
+ uint64_t[] lockingProcesses;
+};
+
+} // namespace hal
+
+namespace hal_sandbox {
+
+[ManualDealloc, ChildImpl=virtual, ParentImpl=virtual]
+sync protocol PHal {
+ manager PContent;
+
+child:
+ async NotifyBatteryChange(BatteryInformation aBatteryInfo);
+ async NotifyNetworkChange(NetworkInformation aNetworkInfo);
+ async NotifyWakeLockChange(WakeLockInformation aWakeLockInfo);
+
+parent:
+ async Vibrate(uint32_t[] pattern, uint64_t[] id, PBrowser browser);
+ async CancelVibrate(uint64_t[] id, PBrowser browser);
+
+ async EnableBatteryNotifications();
+ async DisableBatteryNotifications();
+ sync GetCurrentBatteryInformation()
+ returns (BatteryInformation aBatteryInfo);
+
+ async EnableNetworkNotifications();
+ async DisableNetworkNotifications();
+ sync GetCurrentNetworkInformation()
+ returns (NetworkInformation aNetworkInfo);
+
+ async ModifyWakeLock(nsString aTopic,
+ WakeLockControl aLockAdjust,
+ WakeLockControl aHiddenAdjust,
+ uint64_t aProcessID);
+ async EnableWakeLockNotifications();
+ async DisableWakeLockNotifications();
+ sync GetWakeLockInfo(nsString aTopic)
+ returns (WakeLockInformation aWakeLockInfo);
+
+ async LockScreenOrientation(ScreenOrientation aOrientation)
+ returns (nsresult result);
+ async UnlockScreenOrientation();
+
+child:
+ async NotifySensorChange(SensorData aSensorData);
+
+parent:
+ async EnableSensorNotifications(SensorType aSensor);
+ async DisableSensorNotifications(SensorType aSensor);
+
+ async __delete__();
+};
+
+} // namespace hal
+} // namespace mozilla
diff --git a/hal/sandbox/SandboxHal.cpp b/hal/sandbox/SandboxHal.cpp
new file mode 100644
index 0000000000..6564f9fd2a
--- /dev/null
+++ b/hal/sandbox/SandboxHal.cpp
@@ -0,0 +1,341 @@
+/* -*- 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 "HalLog.h"
+#include "mozilla/dom/ContentChild.h"
+#include "mozilla/hal_sandbox/PHalChild.h"
+#include "mozilla/hal_sandbox/PHalParent.h"
+#include "mozilla/dom/BrowserParent.h"
+#include "mozilla/dom/BrowserChild.h"
+#include "mozilla/EnumeratedRange.h"
+#include "mozilla/Observer.h"
+#include "mozilla/Unused.h"
+#include "WindowIdentifier.h"
+
+using namespace mozilla;
+using namespace mozilla::dom;
+using namespace mozilla::hal;
+
+namespace mozilla {
+namespace hal_sandbox {
+
+static bool sHalChildDestroyed = false;
+
+bool HalChildDestroyed() { return sHalChildDestroyed; }
+
+static PHalChild* sHal;
+static PHalChild* Hal() {
+ if (!sHal) {
+ sHal = ContentChild::GetSingleton()->SendPHalConstructor();
+ }
+ return sHal;
+}
+
+void Vibrate(const nsTArray<uint32_t>& pattern, WindowIdentifier&& id) {
+ HAL_LOG("Vibrate: Sending to parent process.");
+
+ WindowIdentifier newID(std::move(id));
+ newID.AppendProcessID();
+ if (BrowserChild* bc = BrowserChild::GetFrom(newID.GetWindow())) {
+ Hal()->SendVibrate(pattern, newID.AsArray(), WrapNotNull(bc));
+ }
+}
+
+void CancelVibrate(WindowIdentifier&& id) {
+ HAL_LOG("CancelVibrate: Sending to parent process.");
+
+ WindowIdentifier newID(std::move(id));
+ newID.AppendProcessID();
+ if (BrowserChild* bc = BrowserChild::GetFrom(newID.GetWindow())) {
+ Hal()->SendCancelVibrate(newID.AsArray(), WrapNotNull(bc));
+ }
+}
+
+void EnableBatteryNotifications() { Hal()->SendEnableBatteryNotifications(); }
+
+void DisableBatteryNotifications() { Hal()->SendDisableBatteryNotifications(); }
+
+void GetCurrentBatteryInformation(BatteryInformation* aBatteryInfo) {
+ Hal()->SendGetCurrentBatteryInformation(aBatteryInfo);
+}
+
+void EnableNetworkNotifications() { Hal()->SendEnableNetworkNotifications(); }
+
+void DisableNetworkNotifications() { Hal()->SendDisableNetworkNotifications(); }
+
+void GetCurrentNetworkInformation(NetworkInformation* aNetworkInfo) {
+ Hal()->SendGetCurrentNetworkInformation(aNetworkInfo);
+}
+
+RefPtr<GenericNonExclusivePromise> LockScreenOrientation(
+ const hal::ScreenOrientation& aOrientation) {
+ return Hal()
+ ->SendLockScreenOrientation(aOrientation)
+ ->Then(GetCurrentSerialEventTarget(), __func__,
+ [](const mozilla::MozPromise<nsresult, ipc::ResponseRejectReason,
+ true>::ResolveOrRejectValue& aValue) {
+ if (aValue.IsResolve()) {
+ if (NS_SUCCEEDED(aValue.ResolveValue())) {
+ return GenericNonExclusivePromise::CreateAndResolve(
+ true, __func__);
+ }
+ return GenericNonExclusivePromise::CreateAndReject(
+ aValue.ResolveValue(), __func__);
+ }
+ return GenericNonExclusivePromise::CreateAndReject(
+ NS_ERROR_FAILURE, __func__);
+ });
+}
+
+void UnlockScreenOrientation() { Hal()->SendUnlockScreenOrientation(); }
+
+void EnableSensorNotifications(SensorType aSensor) {
+ Hal()->SendEnableSensorNotifications(aSensor);
+}
+
+void DisableSensorNotifications(SensorType aSensor) {
+ Hal()->SendDisableSensorNotifications(aSensor);
+}
+
+void EnableWakeLockNotifications() { Hal()->SendEnableWakeLockNotifications(); }
+
+void DisableWakeLockNotifications() {
+ Hal()->SendDisableWakeLockNotifications();
+}
+
+void ModifyWakeLock(const nsAString& aTopic, WakeLockControl aLockAdjust,
+ WakeLockControl aHiddenAdjust, uint64_t aProcessID) {
+ MOZ_ASSERT(aProcessID != CONTENT_PROCESS_ID_UNKNOWN);
+ Hal()->SendModifyWakeLock(aTopic, aLockAdjust, aHiddenAdjust, aProcessID);
+}
+
+void GetWakeLockInfo(const nsAString& aTopic,
+ WakeLockInformation* aWakeLockInfo) {
+ Hal()->SendGetWakeLockInfo(aTopic, aWakeLockInfo);
+}
+
+bool EnableAlarm() {
+ MOZ_CRASH("Alarms can't be programmed from sandboxed contexts. Yet.");
+}
+
+void DisableAlarm() {
+ MOZ_CRASH("Alarms can't be programmed from sandboxed contexts. Yet.");
+}
+
+bool SetAlarm(int32_t aSeconds, int32_t aNanoseconds) {
+ MOZ_CRASH("Alarms can't be programmed from sandboxed contexts. Yet.");
+}
+
+void SetProcessPriority(int aPid, ProcessPriority aPriority) {
+ MOZ_CRASH("Only the main process may set processes' priorities.");
+}
+
+class HalParent : public PHalParent,
+ public BatteryObserver,
+ public NetworkObserver,
+ public ISensorObserver,
+ public WakeLockObserver {
+ public:
+ virtual void ActorDestroy(ActorDestroyReason aWhy) override {
+ // NB: you *must* unconditionally unregister your observer here,
+ // if it *may* be registered below.
+ hal::UnregisterBatteryObserver(this);
+ hal::UnregisterNetworkObserver(this);
+ for (auto sensor : MakeEnumeratedRange(NUM_SENSOR_TYPE)) {
+ hal::UnregisterSensorObserver(sensor, this);
+ }
+ hal::UnregisterWakeLockObserver(this);
+ }
+
+ virtual mozilla::ipc::IPCResult RecvVibrate(
+ nsTArray<unsigned int>&& pattern, nsTArray<uint64_t>&& id,
+ NotNull<PBrowserParent*> browserParent) override {
+ // We give all content vibration permission.
+ // BrowserParent *browserParent = BrowserParent::GetFrom(browserParent);
+ /* xxxkhuey wtf
+ nsCOMPtr<nsIDOMWindow> window =
+ do_QueryInterface(browserParent->GetBrowserDOMWindow());
+ */
+ hal::Vibrate(pattern, WindowIdentifier(std::move(id), nullptr));
+ return IPC_OK();
+ }
+
+ virtual mozilla::ipc::IPCResult RecvCancelVibrate(
+ nsTArray<uint64_t>&& id,
+ NotNull<PBrowserParent*> browserParent) override {
+ // BrowserParent *browserParent = BrowserParent::GetFrom(browserParent);
+ /* XXXkhuey wtf
+ nsCOMPtr<nsIDOMWindow> window =
+ browserParent->GetBrowserDOMWindow();
+ */
+ hal::CancelVibrate(WindowIdentifier(std::move(id), nullptr));
+ return IPC_OK();
+ }
+
+ virtual mozilla::ipc::IPCResult RecvEnableBatteryNotifications() override {
+ // We give all content battery-status permission.
+ hal::RegisterBatteryObserver(this);
+ return IPC_OK();
+ }
+
+ virtual mozilla::ipc::IPCResult RecvDisableBatteryNotifications() override {
+ hal::UnregisterBatteryObserver(this);
+ return IPC_OK();
+ }
+
+ virtual mozilla::ipc::IPCResult RecvGetCurrentBatteryInformation(
+ BatteryInformation* aBatteryInfo) override {
+ // We give all content battery-status permission.
+ hal::GetCurrentBatteryInformation(aBatteryInfo);
+ return IPC_OK();
+ }
+
+ void Notify(const BatteryInformation& aBatteryInfo) override {
+ Unused << SendNotifyBatteryChange(aBatteryInfo);
+ }
+
+ virtual mozilla::ipc::IPCResult RecvEnableNetworkNotifications() override {
+ // We give all content access to this network-status information.
+ hal::RegisterNetworkObserver(this);
+ return IPC_OK();
+ }
+
+ virtual mozilla::ipc::IPCResult RecvDisableNetworkNotifications() override {
+ hal::UnregisterNetworkObserver(this);
+ return IPC_OK();
+ }
+
+ virtual mozilla::ipc::IPCResult RecvGetCurrentNetworkInformation(
+ NetworkInformation* aNetworkInfo) override {
+ hal::GetCurrentNetworkInformation(aNetworkInfo);
+ return IPC_OK();
+ }
+
+ void Notify(const NetworkInformation& aNetworkInfo) override {
+ Unused << SendNotifyNetworkChange(aNetworkInfo);
+ }
+
+ virtual mozilla::ipc::IPCResult RecvLockScreenOrientation(
+ const ScreenOrientation& aOrientation,
+ LockScreenOrientationResolver&& aResolve) override {
+ // FIXME/bug 777980: unprivileged content may only lock
+ // orientation while fullscreen. We should check whether the
+ // request comes from an actor in a process that might be
+ // fullscreen. We don't have that information currently.
+
+ hal::LockScreenOrientation(aOrientation)
+ ->Then(
+ GetMainThreadSerialEventTarget(), __func__,
+ [aResolve](const GenericNonExclusivePromise::ResolveOrRejectValue&
+ aValue) {
+ if (aValue.IsResolve()) {
+ MOZ_ASSERT(aValue.ResolveValue());
+ aResolve(NS_OK);
+ return;
+ }
+ aResolve(aValue.RejectValue());
+ });
+ return IPC_OK();
+ }
+
+ virtual mozilla::ipc::IPCResult RecvUnlockScreenOrientation() override {
+ hal::UnlockScreenOrientation();
+ return IPC_OK();
+ }
+
+ virtual mozilla::ipc::IPCResult RecvEnableSensorNotifications(
+ const SensorType& aSensor) override {
+ // We currently allow any content to register device-sensor
+ // listeners.
+ hal::RegisterSensorObserver(aSensor, this);
+ return IPC_OK();
+ }
+
+ virtual mozilla::ipc::IPCResult RecvDisableSensorNotifications(
+ const SensorType& aSensor) override {
+ hal::UnregisterSensorObserver(aSensor, this);
+ return IPC_OK();
+ }
+
+ void Notify(const SensorData& aSensorData) override {
+ Unused << SendNotifySensorChange(aSensorData);
+ }
+
+ virtual mozilla::ipc::IPCResult RecvModifyWakeLock(
+ const nsAString& aTopic, const WakeLockControl& aLockAdjust,
+ const WakeLockControl& aHiddenAdjust,
+ const uint64_t& aProcessID) override {
+ MOZ_ASSERT(aProcessID != CONTENT_PROCESS_ID_UNKNOWN);
+
+ // We allow arbitrary content to use wake locks.
+ hal::ModifyWakeLock(aTopic, aLockAdjust, aHiddenAdjust, aProcessID);
+ return IPC_OK();
+ }
+
+ virtual mozilla::ipc::IPCResult RecvEnableWakeLockNotifications() override {
+ // We allow arbitrary content to use wake locks.
+ hal::RegisterWakeLockObserver(this);
+ return IPC_OK();
+ }
+
+ virtual mozilla::ipc::IPCResult RecvDisableWakeLockNotifications() override {
+ hal::UnregisterWakeLockObserver(this);
+ return IPC_OK();
+ }
+
+ virtual mozilla::ipc::IPCResult RecvGetWakeLockInfo(
+ const nsAString& aTopic, WakeLockInformation* aWakeLockInfo) override {
+ hal::GetWakeLockInfo(aTopic, aWakeLockInfo);
+ return IPC_OK();
+ }
+
+ void Notify(const WakeLockInformation& aWakeLockInfo) override {
+ Unused << SendNotifyWakeLockChange(aWakeLockInfo);
+ }
+};
+
+class HalChild : public PHalChild {
+ public:
+ virtual void ActorDestroy(ActorDestroyReason aWhy) override {
+ sHalChildDestroyed = true;
+ }
+
+ virtual mozilla::ipc::IPCResult RecvNotifyBatteryChange(
+ const BatteryInformation& aBatteryInfo) override {
+ hal::NotifyBatteryChange(aBatteryInfo);
+ return IPC_OK();
+ }
+
+ virtual mozilla::ipc::IPCResult RecvNotifySensorChange(
+ const hal::SensorData& aSensorData) override;
+
+ virtual mozilla::ipc::IPCResult RecvNotifyNetworkChange(
+ const NetworkInformation& aNetworkInfo) override {
+ hal::NotifyNetworkChange(aNetworkInfo);
+ return IPC_OK();
+ }
+
+ virtual mozilla::ipc::IPCResult RecvNotifyWakeLockChange(
+ const WakeLockInformation& aWakeLockInfo) override {
+ hal::NotifyWakeLockChange(aWakeLockInfo);
+ return IPC_OK();
+ }
+};
+
+mozilla::ipc::IPCResult HalChild::RecvNotifySensorChange(
+ const hal::SensorData& aSensorData) {
+ hal::NotifySensorChange(aSensorData);
+
+ return IPC_OK();
+}
+
+PHalChild* CreateHalChild() { return new HalChild(); }
+
+PHalParent* CreateHalParent() { return new HalParent(); }
+
+} // namespace hal_sandbox
+} // namespace mozilla
diff --git a/hal/sandbox/SandboxHal.h b/hal/sandbox/SandboxHal.h
new file mode 100644
index 0000000000..b735e4da78
--- /dev/null
+++ b/hal/sandbox/SandboxHal.h
@@ -0,0 +1,23 @@
+/* -*- 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/. */
+
+#ifndef mozilla_SandboxHal_h
+#define mozilla_SandboxHal_h
+
+namespace mozilla {
+namespace hal_sandbox {
+
+class PHalChild;
+class PHalParent;
+
+PHalChild* CreateHalChild();
+
+PHalParent* CreateHalParent();
+
+} // namespace hal_sandbox
+} // namespace mozilla
+
+#endif // mozilla_SandboxHal_h
diff --git a/hal/windows/WindowsBattery.cpp b/hal/windows/WindowsBattery.cpp
new file mode 100644
index 0000000000..fba786c8a2
--- /dev/null
+++ b/hal/windows/WindowsBattery.cpp
@@ -0,0 +1,115 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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 "mozilla/Preferences.h"
+#include "mozilla/dom/battery/Constants.h"
+
+#include <windows.h>
+
+using namespace mozilla::dom::battery;
+
+namespace mozilla {
+namespace hal_impl {
+
+static HPOWERNOTIFY sPowerHandle = nullptr;
+static HPOWERNOTIFY sCapacityHandle = nullptr;
+static HWND sHWnd = nullptr;
+
+static LRESULT CALLBACK BatteryWindowProc(HWND hwnd, UINT msg, WPARAM wParam,
+ LPARAM lParam) {
+ if (msg != WM_POWERBROADCAST || wParam != PBT_POWERSETTINGCHANGE) {
+ return DefWindowProc(hwnd, msg, wParam, lParam);
+ }
+
+ hal::BatteryInformation currentInfo;
+
+ // Since we need update remainingTime, we cannot use LPARAM.
+ hal_impl::GetCurrentBatteryInformation(&currentInfo);
+
+ hal::NotifyBatteryChange(currentInfo);
+ return TRUE;
+}
+
+void EnableBatteryNotifications() {
+ // Create custom window to watch battery event
+ // If we can get Gecko's window handle, this is unnecessary.
+
+ if (sHWnd == nullptr) {
+ WNDCLASSW wc;
+ HMODULE hSelf = GetModuleHandle(nullptr);
+
+ if (!GetClassInfoW(hSelf, L"MozillaBatteryClass", &wc)) {
+ ZeroMemory(&wc, sizeof(WNDCLASSW));
+ wc.hInstance = hSelf;
+ wc.lpfnWndProc = BatteryWindowProc;
+ wc.lpszClassName = L"MozillaBatteryClass";
+ RegisterClassW(&wc);
+ }
+
+ sHWnd = CreateWindowW(L"MozillaBatteryClass", L"Battery Watcher", 0, 0, 0,
+ 0, 0, nullptr, nullptr, hSelf, nullptr);
+ }
+
+ if (sHWnd == nullptr) {
+ return;
+ }
+
+ sPowerHandle = RegisterPowerSettingNotification(
+ sHWnd, &GUID_ACDC_POWER_SOURCE, DEVICE_NOTIFY_WINDOW_HANDLE);
+ sCapacityHandle = RegisterPowerSettingNotification(
+ sHWnd, &GUID_BATTERY_PERCENTAGE_REMAINING, DEVICE_NOTIFY_WINDOW_HANDLE);
+}
+
+void DisableBatteryNotifications() {
+ if (sPowerHandle) {
+ UnregisterPowerSettingNotification(sPowerHandle);
+ sPowerHandle = nullptr;
+ }
+
+ if (sCapacityHandle) {
+ UnregisterPowerSettingNotification(sCapacityHandle);
+ sCapacityHandle = nullptr;
+ }
+
+ if (sHWnd) {
+ DestroyWindow(sHWnd);
+ sHWnd = nullptr;
+ }
+}
+
+void GetCurrentBatteryInformation(hal::BatteryInformation* aBatteryInfo) {
+ SYSTEM_POWER_STATUS status;
+ if (!GetSystemPowerStatus(&status)) {
+ aBatteryInfo->level() = kDefaultLevel;
+ aBatteryInfo->charging() = kDefaultCharging;
+ aBatteryInfo->remainingTime() = kDefaultRemainingTime;
+ return;
+ }
+
+ aBatteryInfo->level() = status.BatteryLifePercent == 255
+ ? kDefaultLevel
+ : ((double)status.BatteryLifePercent) / 100.0;
+ aBatteryInfo->charging() = (status.ACLineStatus != 0);
+ if (status.ACLineStatus != 0) {
+ if (aBatteryInfo->level() == 1.0) {
+ // GetSystemPowerStatus API may returns -1 for BatteryFullLifeTime.
+ // So, if battery is 100%, set kDefaultRemainingTime at force.
+ aBatteryInfo->remainingTime() = kDefaultRemainingTime;
+ } else {
+ aBatteryInfo->remainingTime() = status.BatteryFullLifeTime == (DWORD)-1
+ ? kUnknownRemainingTime
+ : status.BatteryFullLifeTime;
+ }
+ } else {
+ aBatteryInfo->remainingTime() = status.BatteryLifeTime == (DWORD)-1
+ ? kUnknownRemainingTime
+ : status.BatteryLifeTime;
+ }
+}
+
+} // namespace hal_impl
+} // namespace mozilla
diff --git a/hal/windows/WindowsProcessPriority.cpp b/hal/windows/WindowsProcessPriority.cpp
new file mode 100644
index 0000000000..3f2153f569
--- /dev/null
+++ b/hal/windows/WindowsProcessPriority.cpp
@@ -0,0 +1,85 @@
+/* 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/. */
+
+// SetProcessInformation is only defined for Win8 and newer.
+#if defined(_WIN32_WINNT)
+# undef _WIN32_WINNT
+# define _WIN32_WINNT _WIN32_WINNT_WIN8
+#endif // defined(_WIN32_WINNT)
+
+#include "Hal.h"
+#include "HalLog.h"
+#include "nsWindowsHelpers.h" // for nsAutoHandle and nsModuleHandle
+#include "mozilla/StaticPrefs_dom.h"
+
+#include <windows.h>
+
+using namespace mozilla::hal;
+
+namespace mozilla {
+namespace hal_impl {
+
+void SetProcessPriority(int aPid, ProcessPriority aPriority) {
+ HAL_LOG("WindowsProcessPriority - SetProcessPriority(%d, %s)\n", aPid,
+ ProcessPriorityToString(aPriority));
+
+ nsAutoHandle processHandle(
+ ::OpenProcess(PROCESS_SET_INFORMATION, FALSE, aPid));
+ if (processHandle) {
+ DWORD priority = NORMAL_PRIORITY_CLASS;
+ if (aPriority == PROCESS_PRIORITY_BACKGROUND) {
+ priority = IDLE_PRIORITY_CLASS;
+ } else if (aPriority == PROCESS_PRIORITY_BACKGROUND_PERCEIVABLE) {
+ priority = BELOW_NORMAL_PRIORITY_CLASS;
+ }
+
+ if (::SetPriorityClass(processHandle, priority)) {
+ HAL_LOG("WindowsProcessPriority - priority set to %d for pid %d\n",
+ aPriority, aPid);
+ }
+
+ // Set the process into or out of EcoQoS.
+ static bool alreadyInitialized = false;
+ static decltype(::SetProcessInformation)* setProcessInformation = nullptr;
+ if (!alreadyInitialized) {
+ if (aPriority == PROCESS_PRIORITY_PARENT_PROCESS ||
+ !StaticPrefs::dom_ipc_processPriorityManager_backgroundUsesEcoQoS()) {
+ return;
+ }
+
+ alreadyInitialized = true;
+ // SetProcessInformation only exists on Windows 8 and later.
+ nsModuleHandle module(LoadLibrary(L"Kernel32.dll"));
+ if (module) {
+ setProcessInformation =
+ (decltype(::SetProcessInformation)*)GetProcAddress(
+ module, "SetProcessInformation");
+ }
+ }
+ if (!setProcessInformation) {
+ return;
+ }
+
+ PROCESS_POWER_THROTTLING_STATE PowerThrottling;
+ RtlZeroMemory(&PowerThrottling, sizeof(PowerThrottling));
+ PowerThrottling.Version = PROCESS_POWER_THROTTLING_CURRENT_VERSION;
+ PowerThrottling.ControlMask = PROCESS_POWER_THROTTLING_EXECUTION_SPEED;
+ PowerThrottling.StateMask =
+ (aPriority == PROCESS_PRIORITY_BACKGROUND) &&
+ StaticPrefs::
+ dom_ipc_processPriorityManager_backgroundUsesEcoQoS()
+ ? PROCESS_POWER_THROTTLING_EXECUTION_SPEED
+ : 0;
+ if (setProcessInformation(processHandle, ProcessPowerThrottling,
+ &PowerThrottling, sizeof(PowerThrottling))) {
+ HAL_LOG("SetProcessInformation(%d, %s)\n", aPid,
+ aPriority == PROCESS_PRIORITY_BACKGROUND ? "eco" : "normal");
+ } else {
+ HAL_LOG("SetProcessInformation failed for %d\n", aPid);
+ }
+ }
+}
+
+} // namespace hal_impl
+} // namespace mozilla
diff --git a/hal/windows/WindowsScreenConfiguration.cpp b/hal/windows/WindowsScreenConfiguration.cpp
new file mode 100644
index 0000000000..c971054ff2
--- /dev/null
+++ b/hal/windows/WindowsScreenConfiguration.cpp
@@ -0,0 +1,103 @@
+/* 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/. */
+
+#if defined(WINVER)
+# undef WINVER
+# define WINVER 0x0602
+#endif
+
+#include "Hal.h"
+#include "mozilla/WindowsVersion.h"
+#include "mozilla/widget/ScreenManager.h"
+#include "nsIWindowsUIUtils.h"
+#include "WinUtils.h"
+
+#include <windows.h>
+
+namespace mozilla {
+namespace hal_impl {
+
+static decltype(SetDisplayAutoRotationPreferences)*
+ sSetDisplayAutoRotationPreferences = nullptr;
+
+RefPtr<GenericNonExclusivePromise> LockScreenOrientation(
+ const hal::ScreenOrientation& aOrientation) {
+ // SetDisplayAutoRotationPreferences requires Win8, tablet mode and device
+ // support.
+ if (!IsWin8OrLater()) {
+ return GenericNonExclusivePromise::CreateAndReject(
+ NS_ERROR_DOM_NOT_SUPPORTED_ERR, __func__);
+ }
+ AR_STATE state;
+ if (!widget::WinUtils::GetAutoRotationState(&state)) {
+ return GenericNonExclusivePromise::CreateAndReject(
+ NS_ERROR_DOM_NOT_SUPPORTED_ERR, __func__);
+ }
+
+ if (state & (AR_DISABLED | AR_REMOTESESSION | AR_MULTIMON | AR_NOSENSOR |
+ AR_NOT_SUPPORTED | AR_LAPTOP | AR_DOCKED)) {
+ return GenericNonExclusivePromise::CreateAndReject(
+ NS_ERROR_DOM_NOT_SUPPORTED_ERR, __func__);
+ }
+
+ if (!sSetDisplayAutoRotationPreferences) {
+ HMODULE user32dll = GetModuleHandleW(L"user32.dll");
+ if (user32dll) {
+ sSetDisplayAutoRotationPreferences =
+ (decltype(SetDisplayAutoRotationPreferences)*)GetProcAddress(
+ user32dll, "SetDisplayAutoRotationPreferences");
+ }
+ if (!sSetDisplayAutoRotationPreferences) {
+ return GenericNonExclusivePromise::CreateAndReject(
+ NS_ERROR_DOM_NOT_SUPPORTED_ERR, __func__);
+ }
+ }
+
+ ORIENTATION_PREFERENCE orientation = ORIENTATION_PREFERENCE_NONE;
+
+ if (aOrientation == hal::ScreenOrientation::Default) {
+ // Actually, current screen is single and tablet mode according to
+ // GetAutoRotationState. So get primary screen data for natural orientation.
+ RefPtr<widget::Screen> screen =
+ widget::ScreenManager::GetSingleton().GetPrimaryScreen();
+ hal::ScreenOrientation defaultOrientation =
+ screen->GetDefaultOrientationType();
+ if (defaultOrientation == hal::ScreenOrientation::LandscapePrimary) {
+ orientation = ORIENTATION_PREFERENCE_LANDSCAPE;
+ } else {
+ orientation = ORIENTATION_PREFERENCE_PORTRAIT;
+ }
+ } else {
+ if (aOrientation & hal::ScreenOrientation::LandscapePrimary) {
+ orientation |= ORIENTATION_PREFERENCE_LANDSCAPE;
+ }
+ if (aOrientation & hal::ScreenOrientation::LandscapeSecondary) {
+ orientation |= ORIENTATION_PREFERENCE_LANDSCAPE_FLIPPED;
+ }
+ if (aOrientation & hal::ScreenOrientation::PortraitPrimary) {
+ orientation |= ORIENTATION_PREFERENCE_PORTRAIT;
+ }
+ if (aOrientation & hal::ScreenOrientation::PortraitSecondary) {
+ orientation |= ORIENTATION_PREFERENCE_PORTRAIT_FLIPPED;
+ }
+ }
+
+ if (!sSetDisplayAutoRotationPreferences(orientation)) {
+ return GenericNonExclusivePromise::CreateAndReject(NS_ERROR_DOM_ABORT_ERR,
+ __func__);
+ }
+
+ return GenericNonExclusivePromise::CreateAndResolve(true, __func__);
+}
+
+void UnlockScreenOrientation() {
+ if (!sSetDisplayAutoRotationPreferences) {
+ return;
+ }
+ // This does nothing if the device doesn't support orientation lock
+ sSetDisplayAutoRotationPreferences(ORIENTATION_PREFERENCE_NONE);
+}
+
+} // namespace hal_impl
+} // namespace mozilla
diff --git a/hal/windows/WindowsSensor.cpp b/hal/windows/WindowsSensor.cpp
new file mode 100644
index 0000000000..73bd8bc57c
--- /dev/null
+++ b/hal/windows/WindowsSensor.cpp
@@ -0,0 +1,170 @@
+/* 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 <sensorsapi.h>
+#include <sensors.h>
+#include <portabledevicetypes.h>
+
+#define MEAN_GRAVITY 9.80665
+#define DEFAULT_SENSOR_POLL 100
+
+using namespace mozilla::hal;
+
+namespace mozilla {
+namespace hal_impl {
+
+static RefPtr<ISensor> sAccelerometer;
+
+class SensorEvent final : public ISensorEvents {
+ public:
+ SensorEvent() : mCount(0) {}
+
+ // IUnknown interface
+
+ STDMETHODIMP_(ULONG) AddRef() { return InterlockedIncrement(&mCount); }
+
+ STDMETHODIMP_(ULONG) Release() {
+ ULONG count = InterlockedDecrement(&mCount);
+ if (!count) {
+ delete this;
+ return 0;
+ }
+ return count;
+ }
+
+ STDMETHODIMP QueryInterface(REFIID iid, void** ppv) {
+ if (iid == IID_IUnknown) {
+ *ppv = static_cast<IUnknown*>(this);
+ } else if (iid == IID_ISensorEvents) {
+ *ppv = static_cast<ISensorEvents*>(this);
+ } else {
+ return E_NOINTERFACE;
+ }
+ AddRef();
+ return S_OK;
+ }
+
+ // ISensorEvents interface
+
+ STDMETHODIMP OnEvent(ISensor* aSensor, REFGUID aId,
+ IPortableDeviceValues* aData) {
+ return S_OK;
+ }
+
+ STDMETHODIMP OnLeave(REFSENSOR_ID aId) { return S_OK; }
+
+ STDMETHODIMP OnStateChanged(ISensor* aSensor, SensorState state) {
+ return S_OK;
+ }
+
+ STDMETHODIMP OnDataUpdated(ISensor* aSensor, ISensorDataReport* aReport) {
+ PROPVARIANT v;
+ HRESULT hr;
+ nsTArray<float> values;
+
+ // X-axis acceleration in g's
+ hr = aReport->GetSensorValue(SENSOR_DATA_TYPE_ACCELERATION_X_G, &v);
+ if (FAILED(hr)) {
+ return hr;
+ }
+ values.AppendElement(float(-v.dblVal * MEAN_GRAVITY));
+
+ // Y-axis acceleration in g's
+ hr = aReport->GetSensorValue(SENSOR_DATA_TYPE_ACCELERATION_Y_G, &v);
+ if (FAILED(hr)) {
+ return hr;
+ }
+ values.AppendElement(float(-v.dblVal * MEAN_GRAVITY));
+
+ // Z-axis acceleration in g's
+ hr = aReport->GetSensorValue(SENSOR_DATA_TYPE_ACCELERATION_Z_G, &v);
+ if (FAILED(hr)) {
+ return hr;
+ }
+ values.AppendElement(float(-v.dblVal * MEAN_GRAVITY));
+
+ hal::SensorData sdata(hal::SENSOR_ACCELERATION, PR_Now(), values);
+ hal::NotifySensorChange(sdata);
+
+ return S_OK;
+ }
+
+ private:
+ ULONG mCount;
+};
+
+void EnableSensorNotifications(SensorType aSensor) {
+ if (aSensor != SENSOR_ACCELERATION) {
+ return;
+ }
+
+ if (sAccelerometer) {
+ return;
+ }
+
+ RefPtr<ISensorManager> manager;
+ if (FAILED(CoCreateInstance(CLSID_SensorManager, nullptr,
+ CLSCTX_INPROC_SERVER, IID_ISensorManager,
+ getter_AddRefs(manager)))) {
+ return;
+ }
+
+ // accelerometer event
+
+ RefPtr<ISensorCollection> collection;
+ if (FAILED(manager->GetSensorsByType(SENSOR_TYPE_ACCELEROMETER_3D,
+ getter_AddRefs(collection)))) {
+ return;
+ }
+
+ ULONG count = 0;
+ collection->GetCount(&count);
+ if (!count) {
+ return;
+ }
+
+ RefPtr<ISensor> sensor;
+ collection->GetAt(0, getter_AddRefs(sensor));
+ if (!sensor) {
+ return;
+ }
+
+ // Set report interval to 100ms if possible.
+ // Default value depends on drivers.
+ RefPtr<IPortableDeviceValues> values;
+ if (SUCCEEDED(CoCreateInstance(
+ CLSID_PortableDeviceValues, nullptr, CLSCTX_INPROC_SERVER,
+ IID_IPortableDeviceValues, getter_AddRefs(values)))) {
+ if (SUCCEEDED(values->SetUnsignedIntegerValue(
+ SENSOR_PROPERTY_CURRENT_REPORT_INTERVAL, DEFAULT_SENSOR_POLL))) {
+ RefPtr<IPortableDeviceValues> returns;
+ sensor->SetProperties(values, getter_AddRefs(returns));
+ }
+ }
+
+ RefPtr<SensorEvent> event = new SensorEvent();
+ RefPtr<ISensorEvents> sensorEvents;
+ if (FAILED(event->QueryInterface(IID_ISensorEvents,
+ getter_AddRefs(sensorEvents)))) {
+ return;
+ }
+
+ if (FAILED(sensor->SetEventSink(sensorEvents))) {
+ return;
+ }
+
+ sAccelerometer = sensor;
+}
+
+void DisableSensorNotifications(SensorType aSensor) {
+ if (aSensor == SENSOR_ACCELERATION && sAccelerometer) {
+ sAccelerometer->SetEventSink(nullptr);
+ sAccelerometer = nullptr;
+ }
+}
+
+} // namespace hal_impl
+} // namespace mozilla