diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-21 11:44:51 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-21 11:44:51 +0000 |
commit | 9e3c08db40b8916968b9f30096c7be3f00ce9647 (patch) | |
tree | a68f146d7fa01f0134297619fbe7e33db084e0aa /hal | |
parent | Initial commit. (diff) | |
download | thunderbird-9e3c08db40b8916968b9f30096c7be3f00ce9647.tar.xz thunderbird-9e3c08db40b8916968b9f30096c7be3f00ce9647.zip |
Adding upstream version 1:115.7.0.upstream/1%115.7.0upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'hal')
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, + ¤tCapacity); + + // 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(¤tInfo); + + 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 |