summaryrefslogtreecommitdiffstats
path: root/hal
diff options
context:
space:
mode:
Diffstat (limited to 'hal')
-rw-r--r--hal/Hal.cpp1003
-rw-r--r--hal/Hal.h560
-rw-r--r--hal/HalImpl.h21
-rw-r--r--hal/HalInternal.h105
-rw-r--r--hal/HalLog.h33
-rw-r--r--hal/HalSandbox.h17
-rw-r--r--hal/HalScreenConfiguration.h20
-rw-r--r--hal/HalSensor.h80
-rw-r--r--hal/HalTypes.h177
-rw-r--r--hal/HalWakeLock.cpp282
-rw-r--r--hal/HalWakeLock.h26
-rw-r--r--hal/WindowIdentifier.cpp80
-rw-r--r--hal/WindowIdentifier.h111
-rw-r--r--hal/android/AndroidAlarm.cpp48
-rw-r--r--hal/android/AndroidHal.cpp175
-rw-r--r--hal/android/AndroidSensor.cpp25
-rw-r--r--hal/cocoa/CocoaBattery.cpp325
-rw-r--r--hal/cocoa/CocoaSensor.mm149
-rw-r--r--hal/cocoa/smslib.h159
-rw-r--r--hal/cocoa/smslib.mm938
-rw-r--r--hal/fallback/FallbackAlarm.cpp79
-rw-r--r--hal/fallback/FallbackBattery.cpp30
-rw-r--r--hal/fallback/FallbackDiskSpaceWatcher.cpp19
-rw-r--r--hal/fallback/FallbackFactoryReset.cpp18
-rw-r--r--hal/fallback/FallbackMemory.cpp20
-rw-r--r--hal/fallback/FallbackNetwork.cpp31
-rw-r--r--hal/fallback/FallbackPower.cpp25
-rw-r--r--hal/fallback/FallbackProcessPriority.cpp21
-rw-r--r--hal/fallback/FallbackScreenConfiguration.cpp63
-rw-r--r--hal/fallback/FallbackScreenPower.cpp41
-rw-r--r--hal/fallback/FallbackSensor.cpp23
-rw-r--r--hal/fallback/FallbackSwitch.cpp39
-rw-r--r--hal/fallback/FallbackSystemService.cpp35
-rw-r--r--hal/fallback/FallbackThreadPriority.cpp29
-rw-r--r--hal/fallback/FallbackTime.cpp54
-rw-r--r--hal/fallback/FallbackVibration.cpp23
-rw-r--r--hal/fallback/FallbackWakeLocks.cpp23
-rw-r--r--hal/gonk/GonkDiskSpaceWatcher.cpp324
-rw-r--r--hal/gonk/GonkHal.cpp2045
-rw-r--r--hal/gonk/GonkSensor.cpp861
-rw-r--r--hal/gonk/GonkSensorsHelpers.cpp112
-rw-r--r--hal/gonk/GonkSensorsHelpers.h226
-rw-r--r--hal/gonk/GonkSensorsInterface.cpp494
-rw-r--r--hal/gonk/GonkSensorsInterface.h191
-rw-r--r--hal/gonk/GonkSensorsPollInterface.cpp431
-rw-r--r--hal/gonk/GonkSensorsPollInterface.h340
-rw-r--r--hal/gonk/GonkSensorsRegistryInterface.cpp213
-rw-r--r--hal/gonk/GonkSensorsRegistryInterface.h182
-rw-r--r--hal/gonk/GonkSwitch.cpp479
-rw-r--r--hal/gonk/SensorsTypes.h140
-rw-r--r--hal/gonk/SystemService.cpp131
-rw-r--r--hal/gonk/UeventPoller.cpp312
-rw-r--r--hal/gonk/UeventPoller.h49
-rw-r--r--hal/gonk/fanotify.h118
-rw-r--r--hal/gonk/nsIRecoveryService.idl39
-rw-r--r--hal/gonk/tavarua.h484
-rw-r--r--hal/linux/LinuxMemory.cpp39
-rw-r--r--hal/linux/LinuxPower.cpp167
-rw-r--r--hal/linux/UPowerClient.cpp508
-rw-r--r--hal/moz.build185
-rw-r--r--hal/sandbox/PHal.ipdl159
-rw-r--r--hal/sandbox/SandboxHal.cpp907
-rw-r--r--hal/sandbox/SandboxHal.h23
-rw-r--r--hal/windows/WindowsBattery.cpp190
-rw-r--r--hal/windows/WindowsSensor.cpp184
65 files changed, 14440 insertions, 0 deletions
diff --git a/hal/Hal.cpp b/hal/Hal.cpp
new file mode 100644
index 000000000..44be2f510
--- /dev/null
+++ b/hal/Hal.cpp
@@ -0,0 +1,1003 @@
+/* -*- 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 "nsIDOMDocument.h"
+#include "nsIDOMWindow.h"
+#include "nsIDocument.h"
+#include "nsIDocShell.h"
+#include "nsITabChild.h"
+#include "nsIWebNavigation.h"
+#include "nsThreadUtils.h"
+#include "nsXULAppAPI.h"
+#include "nsPIDOMWindow.h"
+#include "nsJSUtils.h"
+#include "mozilla/ClearOnShutdown.h"
+#include "mozilla/Observer.h"
+#include "mozilla/Services.h"
+#include "mozilla/StaticPtr.h"
+#include "mozilla/dom/ContentChild.h"
+#include "mozilla/dom/ContentParent.h"
+#include "mozilla/dom/ScreenOrientation.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 {
+namespace hal {
+
+mozilla::LogModule *
+GetHalLog()
+{
+ static mozilla::LazyLogModule sHalLog("hal");
+ return sHalLog;
+}
+
+namespace {
+
+void
+AssertMainThread()
+{
+ MOZ_ASSERT(NS_IsMainThread());
+}
+
+bool
+InSandbox()
+{
+ return GeckoProcessType_Content == XRE_GetProcessType();
+}
+
+void
+AssertMainProcess()
+{
+ MOZ_ASSERT(GeckoProcessType_Default == XRE_GetProcessType());
+}
+
+#if !defined(MOZ_WIDGET_GONK)
+
+bool
+WindowIsActive(nsPIDOMWindowInner* aWindow)
+{
+ nsIDocument* document = aWindow->GetDoc();
+ NS_ENSURE_TRUE(document, false);
+
+ return !document->Hidden();
+}
+
+#endif // !defined(MOZ_WIDGET_GONK)
+
+StaticAutoPtr<WindowIdentifier::IDArrayType> gLastIDToVibrate;
+
+void InitLastIDToVibrate()
+{
+ gLastIDToVibrate = new WindowIdentifier::IDArrayType();
+ ClearOnShutdown(&gLastIDToVibrate);
+}
+
+} // namespace
+
+void
+Vibrate(const nsTArray<uint32_t>& pattern, nsPIDOMWindowInner* window)
+{
+ Vibrate(pattern, WindowIdentifier(window));
+}
+
+void
+Vibrate(const nsTArray<uint32_t>& pattern, const WindowIdentifier &id)
+{
+ AssertMainThread();
+
+#if !defined(MOZ_WIDGET_GONK)
+ // 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;
+ }
+#endif // !defined(MOZ_WIDGET_GONK)
+
+ if (!InSandbox()) {
+ if (!gLastIDToVibrate) {
+ InitLastIDToVibrate();
+ }
+ *gLastIDToVibrate = id.AsArray();
+ }
+
+ // 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() ? id : WindowIdentifier()));
+}
+
+void
+CancelVibrate(nsPIDOMWindowInner* window)
+{
+ CancelVibrate(WindowIdentifier(window));
+}
+
+void
+CancelVibrate(const WindowIdentifier &id)
+{
+ AssertMainThread();
+
+ // 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.
+
+ if (InSandbox() || (gLastIDToVibrate && *gLastIDToVibrate == id.AsArray())) {
+ // 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() ? id : WindowIdentifier()));
+ }
+}
+
+template <class InfoType>
+class ObserversManager
+{
+public:
+ void AddObserver(Observer<InfoType>* aObserver) {
+ if (!mObservers) {
+ mObservers = new mozilla::ObserverList<InfoType>();
+ }
+
+ mObservers->AddObserver(aObserver);
+
+ if (mObservers->Length() == 1) {
+ EnableNotifications();
+ }
+ }
+
+ void RemoveObserver(Observer<InfoType>* aObserver) {
+ bool removed = mObservers && mObservers->RemoveObserver(aObserver);
+ if (!removed) {
+ return;
+ }
+
+ if (mObservers->Length() == 0) {
+ DisableNotifications();
+
+ OnNotificationsDisabled();
+
+ delete mObservers;
+ mObservers = nullptr;
+ }
+ }
+
+ void BroadcastInformation(const InfoType& aInfo) {
+ // It is possible for mObservers to be nullptr here on some platforms,
+ // because a call to BroadcastInformation gets queued up asynchronously
+ // while RemoveObserver is running (and before the notifications are
+ // disabled). The queued call can then get run after mObservers has
+ // been nulled out. See bug 757025.
+ if (!mObservers) {
+ return;
+ }
+ mObservers->Broadcast(aInfo);
+ }
+
+protected:
+ 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;
+
+ virtual void OnNotificationsDisabled() {
+ mHasValidCache = false;
+ }
+
+private:
+ InfoType mInfo;
+ bool mHasValidCache;
+};
+
+class BatteryObserversManager : public CachingObserversManager<BatteryInformation>
+{
+protected:
+ void EnableNotifications() {
+ PROXY_IF_SANDBOXED(EnableBatteryNotifications());
+ }
+
+ void DisableNotifications() {
+ PROXY_IF_SANDBOXED(DisableBatteryNotifications());
+ }
+
+ void GetCurrentInformationInternal(BatteryInformation* aInfo) {
+ PROXY_IF_SANDBOXED(GetCurrentBatteryInformation(aInfo));
+ }
+};
+
+static BatteryObserversManager&
+BatteryObservers()
+{
+ static BatteryObserversManager sBatteryObservers;
+ AssertMainThread();
+ return sBatteryObservers;
+}
+
+class NetworkObserversManager : public CachingObserversManager<NetworkInformation>
+{
+protected:
+ void EnableNotifications() {
+ PROXY_IF_SANDBOXED(EnableNetworkNotifications());
+ }
+
+ void DisableNotifications() {
+ PROXY_IF_SANDBOXED(DisableNetworkNotifications());
+ }
+
+ void GetCurrentInformationInternal(NetworkInformation* aInfo) {
+ PROXY_IF_SANDBOXED(GetCurrentNetworkInformation(aInfo));
+ }
+};
+
+static NetworkObserversManager&
+NetworkObservers()
+{
+ static NetworkObserversManager sNetworkObservers;
+ AssertMainThread();
+ return sNetworkObservers;
+}
+
+class WakeLockObserversManager : public ObserversManager<WakeLockInformation>
+{
+protected:
+ void EnableNotifications() {
+ PROXY_IF_SANDBOXED(EnableWakeLockNotifications());
+ }
+
+ void DisableNotifications() {
+ PROXY_IF_SANDBOXED(DisableWakeLockNotifications());
+ }
+};
+
+static WakeLockObserversManager&
+WakeLockObservers()
+{
+ static WakeLockObserversManager sWakeLockObservers;
+ AssertMainThread();
+ return sWakeLockObservers;
+}
+
+class ScreenConfigurationObserversManager : public CachingObserversManager<ScreenConfiguration>
+{
+protected:
+ void EnableNotifications() {
+ PROXY_IF_SANDBOXED(EnableScreenConfigurationNotifications());
+ }
+
+ void DisableNotifications() {
+ PROXY_IF_SANDBOXED(DisableScreenConfigurationNotifications());
+ }
+
+ void GetCurrentInformationInternal(ScreenConfiguration* aInfo) {
+ PROXY_IF_SANDBOXED(GetCurrentScreenConfiguration(aInfo));
+ }
+};
+
+static ScreenConfigurationObserversManager&
+ScreenConfigurationObservers()
+{
+ AssertMainThread();
+ static ScreenConfigurationObserversManager sScreenConfigurationObservers;
+ return sScreenConfigurationObservers;
+}
+
+void
+RegisterBatteryObserver(BatteryObserver* aObserver)
+{
+ AssertMainThread();
+ BatteryObservers().AddObserver(aObserver);
+}
+
+void
+UnregisterBatteryObserver(BatteryObserver* aObserver)
+{
+ AssertMainThread();
+ BatteryObservers().RemoveObserver(aObserver);
+}
+
+void
+GetCurrentBatteryInformation(BatteryInformation* aInfo)
+{
+ AssertMainThread();
+ *aInfo = BatteryObservers().GetCurrentInformation();
+}
+
+void
+NotifyBatteryChange(const BatteryInformation& aInfo)
+{
+ AssertMainThread();
+ BatteryObservers().CacheInformation(aInfo);
+ BatteryObservers().BroadcastCachedInformation();
+}
+
+bool GetScreenEnabled()
+{
+ AssertMainThread();
+ RETURN_PROXY_IF_SANDBOXED(GetScreenEnabled(), false);
+}
+
+void SetScreenEnabled(bool aEnabled)
+{
+ AssertMainThread();
+ PROXY_IF_SANDBOXED(SetScreenEnabled(aEnabled));
+}
+
+bool GetKeyLightEnabled()
+{
+ AssertMainThread();
+ RETURN_PROXY_IF_SANDBOXED(GetKeyLightEnabled(), false);
+}
+
+void SetKeyLightEnabled(bool aEnabled)
+{
+ AssertMainThread();
+ PROXY_IF_SANDBOXED(SetKeyLightEnabled(aEnabled));
+}
+
+bool GetCpuSleepAllowed()
+{
+ // Generally for interfaces that are accessible by normal web content
+ // we should cache the result and be notified on state changes, like
+ // what the battery API does. But since this is only used by
+ // privileged interface, the synchronous getter is OK here.
+ AssertMainThread();
+ RETURN_PROXY_IF_SANDBOXED(GetCpuSleepAllowed(), true);
+}
+
+void SetCpuSleepAllowed(bool aAllowed)
+{
+ AssertMainThread();
+ PROXY_IF_SANDBOXED(SetCpuSleepAllowed(aAllowed));
+}
+
+double GetScreenBrightness()
+{
+ AssertMainThread();
+ RETURN_PROXY_IF_SANDBOXED(GetScreenBrightness(), 0);
+}
+
+void SetScreenBrightness(double aBrightness)
+{
+ AssertMainThread();
+ PROXY_IF_SANDBOXED(SetScreenBrightness(clamped(aBrightness, 0.0, 1.0)));
+}
+
+class SystemClockChangeObserversManager : public ObserversManager<int64_t>
+{
+protected:
+ void EnableNotifications() {
+ PROXY_IF_SANDBOXED(EnableSystemClockChangeNotifications());
+ }
+
+ void DisableNotifications() {
+ PROXY_IF_SANDBOXED(DisableSystemClockChangeNotifications());
+ }
+};
+
+static SystemClockChangeObserversManager&
+SystemClockChangeObservers()
+{
+ static SystemClockChangeObserversManager sSystemClockChangeObservers;
+ AssertMainThread();
+ return sSystemClockChangeObservers;
+}
+
+void
+RegisterSystemClockChangeObserver(SystemClockChangeObserver* aObserver)
+{
+ AssertMainThread();
+ SystemClockChangeObservers().AddObserver(aObserver);
+}
+
+void
+UnregisterSystemClockChangeObserver(SystemClockChangeObserver* aObserver)
+{
+ AssertMainThread();
+ SystemClockChangeObservers().RemoveObserver(aObserver);
+}
+
+void
+NotifySystemClockChange(const int64_t& aClockDeltaMS)
+{
+ SystemClockChangeObservers().BroadcastInformation(aClockDeltaMS);
+}
+
+class SystemTimezoneChangeObserversManager : public ObserversManager<SystemTimezoneChangeInformation>
+{
+protected:
+ void EnableNotifications() {
+ PROXY_IF_SANDBOXED(EnableSystemTimezoneChangeNotifications());
+ }
+
+ void DisableNotifications() {
+ PROXY_IF_SANDBOXED(DisableSystemTimezoneChangeNotifications());
+ }
+};
+
+static SystemTimezoneChangeObserversManager&
+SystemTimezoneChangeObservers()
+{
+ static SystemTimezoneChangeObserversManager sSystemTimezoneChangeObservers;
+ return sSystemTimezoneChangeObservers;
+}
+
+void
+RegisterSystemTimezoneChangeObserver(SystemTimezoneChangeObserver* aObserver)
+{
+ AssertMainThread();
+ SystemTimezoneChangeObservers().AddObserver(aObserver);
+}
+
+void
+UnregisterSystemTimezoneChangeObserver(SystemTimezoneChangeObserver* aObserver)
+{
+ AssertMainThread();
+ SystemTimezoneChangeObservers().RemoveObserver(aObserver);
+}
+
+void
+NotifySystemTimezoneChange(const SystemTimezoneChangeInformation& aSystemTimezoneChangeInfo)
+{
+ nsJSUtils::ResetTimeZone();
+ SystemTimezoneChangeObservers().BroadcastInformation(aSystemTimezoneChangeInfo);
+}
+
+void
+AdjustSystemClock(int64_t aDeltaMilliseconds)
+{
+ AssertMainThread();
+ PROXY_IF_SANDBOXED(AdjustSystemClock(aDeltaMilliseconds));
+}
+
+void
+SetTimezone(const nsCString& aTimezoneSpec)
+{
+ AssertMainThread();
+ PROXY_IF_SANDBOXED(SetTimezone(aTimezoneSpec));
+}
+
+int32_t
+GetTimezoneOffset()
+{
+ AssertMainThread();
+ RETURN_PROXY_IF_SANDBOXED(GetTimezoneOffset(), 0);
+}
+
+nsCString
+GetTimezone()
+{
+ AssertMainThread();
+ RETURN_PROXY_IF_SANDBOXED(GetTimezone(), nsCString(""));
+}
+
+void
+EnableSensorNotifications(SensorType aSensor) {
+ AssertMainThread();
+ PROXY_IF_SANDBOXED(EnableSensorNotifications(aSensor));
+}
+
+void
+DisableSensorNotifications(SensorType aSensor) {
+ AssertMainThread();
+ PROXY_IF_SANDBOXED(DisableSensorNotifications(aSensor));
+}
+
+typedef mozilla::ObserverList<SensorData> SensorObserverList;
+static SensorObserverList* gSensorObservers = nullptr;
+
+static SensorObserverList &
+GetSensorObservers(SensorType sensor_type) {
+ MOZ_ASSERT(sensor_type < NUM_SENSOR_TYPE);
+
+ if(!gSensorObservers) {
+ gSensorObservers = new SensorObserverList[NUM_SENSOR_TYPE];
+ }
+ return gSensorObservers[sensor_type];
+}
+
+void
+RegisterSensorObserver(SensorType aSensor, ISensorObserver *aObserver) {
+ SensorObserverList &observers = GetSensorObservers(aSensor);
+
+ AssertMainThread();
+
+ observers.AddObserver(aObserver);
+ if(observers.Length() == 1) {
+ EnableSensorNotifications(aSensor);
+ }
+}
+
+void
+UnregisterSensorObserver(SensorType aSensor, ISensorObserver *aObserver) {
+ AssertMainThread();
+
+ if (!gSensorObservers) {
+ return;
+ }
+
+ SensorObserverList &observers = GetSensorObservers(aSensor);
+ if (!observers.RemoveObserver(aObserver) || observers.Length() > 0) {
+ return;
+ }
+ DisableSensorNotifications(aSensor);
+
+ // Destroy sSensorObservers only if all observer lists are empty.
+ for (int i = 0; i < NUM_SENSOR_TYPE; i++) {
+ if (gSensorObservers[i].Length() > 0) {
+ return;
+ }
+ }
+ delete [] gSensorObservers;
+ gSensorObservers = nullptr;
+}
+
+void
+NotifySensorChange(const SensorData &aSensorData) {
+ SensorObserverList &observers = GetSensorObservers(aSensorData.sensor());
+
+ AssertMainThread();
+
+ observers.Broadcast(aSensorData);
+}
+
+void
+RegisterNetworkObserver(NetworkObserver* aObserver)
+{
+ AssertMainThread();
+ NetworkObservers().AddObserver(aObserver);
+}
+
+void
+UnregisterNetworkObserver(NetworkObserver* aObserver)
+{
+ AssertMainThread();
+ NetworkObservers().RemoveObserver(aObserver);
+}
+
+void
+GetCurrentNetworkInformation(NetworkInformation* aInfo)
+{
+ AssertMainThread();
+ *aInfo = NetworkObservers().GetCurrentInformation();
+}
+
+void
+NotifyNetworkChange(const NetworkInformation& aInfo)
+{
+ NetworkObservers().CacheInformation(aInfo);
+ NetworkObservers().BroadcastCachedInformation();
+}
+
+void Reboot()
+{
+ AssertMainProcess();
+ AssertMainThread();
+ PROXY_IF_SANDBOXED(Reboot());
+}
+
+void PowerOff()
+{
+ AssertMainProcess();
+ AssertMainThread();
+ PROXY_IF_SANDBOXED(PowerOff());
+}
+
+void StartForceQuitWatchdog(ShutdownMode aMode, int32_t aTimeoutSecs)
+{
+ AssertMainProcess();
+ AssertMainThread();
+ PROXY_IF_SANDBOXED(StartForceQuitWatchdog(aMode, aTimeoutSecs));
+}
+
+void
+RegisterWakeLockObserver(WakeLockObserver* aObserver)
+{
+ AssertMainThread();
+ WakeLockObservers().AddObserver(aObserver);
+}
+
+void
+UnregisterWakeLockObserver(WakeLockObserver* aObserver)
+{
+ AssertMainThread();
+ WakeLockObservers().RemoveObserver(aObserver);
+}
+
+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);
+}
+
+void
+RegisterScreenConfigurationObserver(ScreenConfigurationObserver* aObserver)
+{
+ AssertMainThread();
+ ScreenConfigurationObservers().AddObserver(aObserver);
+}
+
+void
+UnregisterScreenConfigurationObserver(ScreenConfigurationObserver* aObserver)
+{
+ AssertMainThread();
+ ScreenConfigurationObservers().RemoveObserver(aObserver);
+}
+
+void
+GetCurrentScreenConfiguration(ScreenConfiguration* aScreenConfiguration)
+{
+ AssertMainThread();
+ *aScreenConfiguration = ScreenConfigurationObservers().GetCurrentInformation();
+}
+
+void
+NotifyScreenConfigurationChange(const ScreenConfiguration& aScreenConfiguration)
+{
+ ScreenConfigurationObservers().CacheInformation(aScreenConfiguration);
+ ScreenConfigurationObservers().BroadcastCachedInformation();
+}
+
+bool
+LockScreenOrientation(const dom::ScreenOrientationInternal& aOrientation)
+{
+ AssertMainThread();
+ RETURN_PROXY_IF_SANDBOXED(LockScreenOrientation(aOrientation), false);
+}
+
+void
+UnlockScreenOrientation()
+{
+ AssertMainThread();
+ PROXY_IF_SANDBOXED(UnlockScreenOrientation());
+}
+
+void
+EnableSwitchNotifications(SwitchDevice aDevice) {
+ AssertMainThread();
+ PROXY_IF_SANDBOXED(EnableSwitchNotifications(aDevice));
+}
+
+void
+DisableSwitchNotifications(SwitchDevice aDevice) {
+ AssertMainThread();
+ PROXY_IF_SANDBOXED(DisableSwitchNotifications(aDevice));
+}
+
+SwitchState GetCurrentSwitchState(SwitchDevice aDevice)
+{
+ AssertMainThread();
+ RETURN_PROXY_IF_SANDBOXED(GetCurrentSwitchState(aDevice), SWITCH_STATE_UNKNOWN);
+}
+
+void NotifySwitchStateFromInputDevice(SwitchDevice aDevice, SwitchState aState)
+{
+ AssertMainThread();
+ PROXY_IF_SANDBOXED(NotifySwitchStateFromInputDevice(aDevice, aState));
+}
+
+typedef mozilla::ObserverList<SwitchEvent> SwitchObserverList;
+
+static SwitchObserverList *sSwitchObserverLists = nullptr;
+
+static SwitchObserverList&
+GetSwitchObserverList(SwitchDevice aDevice) {
+ MOZ_ASSERT(0 <= aDevice && aDevice < NUM_SWITCH_DEVICE);
+ if (sSwitchObserverLists == nullptr) {
+ sSwitchObserverLists = new SwitchObserverList[NUM_SWITCH_DEVICE];
+ }
+ return sSwitchObserverLists[aDevice];
+}
+
+static void
+ReleaseObserversIfNeeded() {
+ for (int i = 0; i < NUM_SWITCH_DEVICE; i++) {
+ if (sSwitchObserverLists[i].Length() != 0)
+ return;
+ }
+
+ //The length of every list is 0, no observer in the list.
+ delete [] sSwitchObserverLists;
+ sSwitchObserverLists = nullptr;
+}
+
+void
+RegisterSwitchObserver(SwitchDevice aDevice, SwitchObserver *aObserver)
+{
+ AssertMainThread();
+ SwitchObserverList& observer = GetSwitchObserverList(aDevice);
+ observer.AddObserver(aObserver);
+ if (observer.Length() == 1) {
+ EnableSwitchNotifications(aDevice);
+ }
+}
+
+void
+UnregisterSwitchObserver(SwitchDevice aDevice, SwitchObserver *aObserver)
+{
+ AssertMainThread();
+
+ if (!sSwitchObserverLists) {
+ return;
+ }
+
+ SwitchObserverList& observer = GetSwitchObserverList(aDevice);
+ if (!observer.RemoveObserver(aObserver) || observer.Length() > 0) {
+ return;
+ }
+
+ DisableSwitchNotifications(aDevice);
+ ReleaseObserversIfNeeded();
+}
+
+void
+NotifySwitchChange(const SwitchEvent& aEvent)
+{
+ // When callback this notification, main thread may call unregister function
+ // first. We should check if this pointer is valid.
+ if (!sSwitchObserverLists)
+ return;
+
+ SwitchObserverList& observer = GetSwitchObserverList(aEvent.device());
+ observer.Broadcast(aEvent);
+}
+
+static AlarmObserver* sAlarmObserver;
+
+bool
+RegisterTheOneAlarmObserver(AlarmObserver* aObserver)
+{
+ MOZ_ASSERT(!InSandbox());
+ MOZ_ASSERT(!sAlarmObserver);
+
+ sAlarmObserver = aObserver;
+ RETURN_PROXY_IF_SANDBOXED(EnableAlarm(), false);
+}
+
+void
+UnregisterTheOneAlarmObserver()
+{
+ if (sAlarmObserver) {
+ sAlarmObserver = nullptr;
+ PROXY_IF_SANDBOXED(DisableAlarm());
+ }
+}
+
+void
+NotifyAlarmFired()
+{
+ if (sAlarmObserver) {
+ sAlarmObserver->Notify(void_t());
+ }
+}
+
+bool
+SetAlarm(int32_t aSeconds, int32_t aNanoseconds)
+{
+ // It's pointless to program an alarm nothing is going to observe ...
+ MOZ_ASSERT(sAlarmObserver);
+ RETURN_PROXY_IF_SANDBOXED(SetAlarm(aSeconds, aNanoseconds), false);
+}
+
+void
+SetProcessPriority(int aPid, ProcessPriority aPriority, uint32_t aLRU)
+{
+ // n.b. The sandboxed implementation crashes; SetProcessPriority works only
+ // from the main process.
+ PROXY_IF_SANDBOXED(SetProcessPriority(aPid, aPriority, aLRU));
+}
+
+void
+SetCurrentThreadPriority(hal::ThreadPriority aThreadPriority)
+{
+ PROXY_IF_SANDBOXED(SetCurrentThreadPriority(aThreadPriority));
+}
+
+void
+SetThreadPriority(PlatformThreadId aThreadId,
+ hal::ThreadPriority aThreadPriority)
+{
+ PROXY_IF_SANDBOXED(SetThreadPriority(aThreadId, aThreadPriority));
+}
+
+// From HalTypes.h.
+const char*
+ProcessPriorityToString(ProcessPriority aPriority)
+{
+ switch (aPriority) {
+ case PROCESS_PRIORITY_MASTER:
+ return "MASTER";
+ 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 "???";
+ }
+}
+
+const char *
+ThreadPriorityToString(ThreadPriority aPriority)
+{
+ switch (aPriority) {
+ case THREAD_PRIORITY_COMPOSITOR:
+ return "COMPOSITOR";
+ default:
+ MOZ_ASSERT(false);
+ return "???";
+ }
+}
+
+void FactoryReset(mozilla::dom::FactoryResetReason& aReason)
+{
+ AssertMainThread();
+ PROXY_IF_SANDBOXED(FactoryReset(aReason));
+}
+
+void
+StartDiskSpaceWatcher()
+{
+ AssertMainProcess();
+ AssertMainThread();
+ PROXY_IF_SANDBOXED(StartDiskSpaceWatcher());
+}
+
+void
+StopDiskSpaceWatcher()
+{
+ AssertMainProcess();
+ AssertMainThread();
+ PROXY_IF_SANDBOXED(StopDiskSpaceWatcher());
+}
+
+uint32_t
+GetTotalSystemMemory()
+{
+ return hal_impl::GetTotalSystemMemory();
+}
+
+bool IsHeadphoneEventFromInputDev()
+{
+ AssertMainThread();
+ RETURN_PROXY_IF_SANDBOXED(IsHeadphoneEventFromInputDev(), false);
+}
+
+nsresult StartSystemService(const char* aSvcName, const char* aArgs)
+{
+ AssertMainThread();
+ RETURN_PROXY_IF_SANDBOXED(StartSystemService(aSvcName, aArgs), NS_ERROR_FAILURE);
+}
+
+void StopSystemService(const char* aSvcName)
+{
+ AssertMainThread();
+ PROXY_IF_SANDBOXED(StopSystemService(aSvcName));
+}
+
+bool SystemServiceIsRunning(const char* aSvcName)
+{
+ AssertMainThread();
+ RETURN_PROXY_IF_SANDBOXED(SystemServiceIsRunning(aSvcName), false);
+}
+
+} // namespace hal
+} // namespace mozilla
diff --git a/hal/Hal.h b/hal/Hal.h
new file mode 100644
index 000000000..e45bbcde2
--- /dev/null
+++ b/hal/Hal.h
@@ -0,0 +1,560 @@
+/* -*- 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 "base/basictypes.h"
+#include "base/platform_thread.h"
+#include "nsTArray.h"
+#include "mozilla/dom/battery/Types.h"
+#include "mozilla/dom/MozPowerManagerBinding.h"
+#include "mozilla/dom/network/Types.h"
+#include "mozilla/dom/power/Types.h"
+#include "mozilla/dom/ScreenOrientation.h"
+#include "mozilla/hal_sandbox/PHal.h"
+#include "mozilla/HalScreenConfiguration.h"
+#include "mozilla/HalTypes.h"
+#include "mozilla/Observer.h"
+#include "mozilla/Types.h"
+
+/*
+ * 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 {
+
+typedef Observer<void_t> AlarmObserver;
+
+class WindowIdentifier;
+
+typedef Observer<int64_t> SystemClockChangeObserver;
+typedef Observer<SystemTimezoneChangeInformation> SystemTimezoneChangeObserver;
+
+} // namespace hal
+
+namespace MOZ_HAL_NAMESPACE {
+
+/**
+ * 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,
+ const 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(const hal::WindowIdentifier &id);
+
+/**
+ * Inform the battery backend there is a new battery observer.
+ * @param aBatteryObserver The observer that should be added.
+ */
+void RegisterBatteryObserver(BatteryObserver* aBatteryObserver);
+
+/**
+ * Inform the battery backend a battery observer unregistered.
+ * @param aBatteryObserver The observer that should be removed.
+ */
+void UnregisterBatteryObserver(BatteryObserver* aBatteryObserver);
+
+/**
+ * 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);
+
+/**
+ * Determine whether the device's screen is currently enabled.
+ */
+bool GetScreenEnabled();
+
+/**
+ * Enable or disable the device's screen.
+ *
+ * Note that it may take a few seconds for the screen to turn on or off.
+ */
+void SetScreenEnabled(bool aEnabled);
+
+/**
+ * Determine whether the device's keypad/button backlight is currently enabled.
+ */
+bool GetKeyLightEnabled();
+
+/**
+ * Enable or disable the device's keypad/button backlight.
+ */
+void SetKeyLightEnabled(bool aEnabled);
+
+/**
+ * Get the brightness of the device's screen's backlight, on a scale from 0
+ * (very dim) to 1 (full blast).
+ *
+ * If the display is currently disabled, this returns the brightness the
+ * backlight will have when the display is re-enabled.
+ */
+double GetScreenBrightness();
+
+/**
+ * Set the brightness of the device's screen's backlight, on a scale from 0
+ * (very dimm) to 1 (full blast). Values larger than 1 are treated like 1, and
+ * values smaller than 0 are treated like 0.
+ *
+ * Note that we may reduce the resolution of the given brightness value before
+ * sending it to the screen. Therefore if you call SetScreenBrightness(x)
+ * followed by GetScreenBrightness(), the value returned by
+ * GetScreenBrightness() may not be exactly x.
+ */
+void SetScreenBrightness(double aBrightness);
+
+/**
+ * Determine whether the device is allowed to sleep.
+ */
+bool GetCpuSleepAllowed();
+
+/**
+ * Set whether the device is allowed to suspend automatically after
+ * the screen is disabled.
+ */
+void SetCpuSleepAllowed(bool aAllowed);
+
+/**
+ * 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);
+
+
+/**
+ * Inform the network backend there is a new network observer.
+ * @param aNetworkObserver The observer that should be added.
+ */
+void RegisterNetworkObserver(NetworkObserver* aNetworkObserver);
+
+/**
+ * Inform the network backend a network observer unregistered.
+ * @param aNetworkObserver The observer that should be removed.
+ */
+void UnregisterNetworkObserver(NetworkObserver* aNetworkObserver);
+
+/**
+ * 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);
+
+/**
+ * Adjusting system clock.
+ * @param aDeltaMilliseconds The difference compared with current system clock.
+ */
+void AdjustSystemClock(int64_t aDeltaMilliseconds);
+
+/**
+ * Set timezone
+ * @param aTimezoneSpec The definition can be found in
+ * http://en.wikipedia.org/wiki/List_of_tz_database_time_zones
+ */
+void SetTimezone(const nsCString& aTimezoneSpec);
+
+/**
+ * Get timezone
+ * http://en.wikipedia.org/wiki/List_of_tz_database_time_zones
+ */
+nsCString GetTimezone();
+
+/**
+ * Get timezone offset
+ * returns the timezone offset relative to UTC in minutes (DST effect included)
+ */
+int32_t GetTimezoneOffset();
+
+/**
+ * Register observer for system clock changed notification.
+ * @param aObserver The observer that should be added.
+ */
+void RegisterSystemClockChangeObserver(
+ hal::SystemClockChangeObserver* aObserver);
+
+/**
+ * Unregister the observer for system clock changed.
+ * @param aObserver The observer that should be removed.
+ */
+void UnregisterSystemClockChangeObserver(
+ hal::SystemClockChangeObserver* aObserver);
+
+/**
+ * Notify of a change in the system clock.
+ * @param aClockDeltaMS
+ */
+void NotifySystemClockChange(const int64_t& aClockDeltaMS);
+
+/**
+ * Register observer for system timezone changed notification.
+ * @param aObserver The observer that should be added.
+ */
+void RegisterSystemTimezoneChangeObserver(
+ hal::SystemTimezoneChangeObserver* aObserver);
+
+/**
+ * Unregister the observer for system timezone changed.
+ * @param aObserver The observer that should be removed.
+ */
+void UnregisterSystemTimezoneChangeObserver(
+ hal::SystemTimezoneChangeObserver* aObserver);
+
+/**
+ * Notify of a change in the system timezone.
+ * @param aSystemTimezoneChangeInfo
+ */
+void NotifySystemTimezoneChange(
+ const hal::SystemTimezoneChangeInformation& aSystemTimezoneChangeInfo);
+
+/**
+ * Reboot the device.
+ *
+ * This API is currently only allowed to be used from the main process.
+ */
+void Reboot();
+
+/**
+ * Power off the device.
+ *
+ * This API is currently only allowed to be used from the main process.
+ */
+void PowerOff();
+
+/**
+ * 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();
+
+/**
+ * Inform the wake lock backend there is a new wake lock observer.
+ * @param aWakeLockObserver The observer that should be added.
+ */
+void RegisterWakeLockObserver(WakeLockObserver* aObserver);
+
+/**
+ * Inform the wake lock backend a wake lock observer unregistered.
+ * @param aWakeLockObserver The observer that should be removed.
+ */
+void UnregisterWakeLockObserver(WakeLockObserver* aObserver);
+
+/**
+ * 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);
+
+/**
+ * Inform the backend there is a new screen configuration observer.
+ * @param aScreenConfigurationObserver The observer that should be added.
+ */
+void RegisterScreenConfigurationObserver(hal::ScreenConfigurationObserver* aScreenConfigurationObserver);
+
+/**
+ * Inform the backend a screen configuration observer unregistered.
+ * @param aScreenConfigurationObserver The observer that should be removed.
+ */
+void UnregisterScreenConfigurationObserver(hal::ScreenConfigurationObserver* aScreenConfigurationObserver);
+
+/**
+ * Returns the current screen configuration.
+ */
+void GetCurrentScreenConfiguration(hal::ScreenConfiguration* aScreenConfiguration);
+
+/**
+ * Notify of a change in the screen configuration.
+ * @param aScreenConfiguration The new screen orientation.
+ */
+void NotifyScreenConfigurationChange(const hal::ScreenConfiguration& aScreenConfiguration);
+
+/**
+ * Lock the screen orientation to the specific orientation.
+ * @return Whether the lock has been accepted.
+ */
+MOZ_MUST_USE bool LockScreenOrientation(const dom::ScreenOrientationInternal& aOrientation);
+
+/**
+ * Unlock the screen orientation.
+ */
+void UnlockScreenOrientation();
+
+/**
+ * Register an observer for the switch of given SwitchDevice.
+ *
+ * The observer will receive data whenever the data generated by the
+ * given switch.
+ */
+void RegisterSwitchObserver(hal::SwitchDevice aDevice, hal::SwitchObserver *aSwitchObserver);
+
+/**
+ * Unregister an observer for the switch of given SwitchDevice.
+ */
+void UnregisterSwitchObserver(hal::SwitchDevice aDevice, hal::SwitchObserver *aSwitchObserver);
+
+/**
+ * Notify the state of the switch.
+ *
+ * This API is internal to hal; clients shouldn't call it directly.
+ */
+void NotifySwitchChange(const hal::SwitchEvent& aEvent);
+
+/**
+ * Get current switch information.
+ */
+hal::SwitchState GetCurrentSwitchState(hal::SwitchDevice aDevice);
+
+/**
+ * Notify switch status change from input device.
+ */
+void NotifySwitchStateFromInputDevice(hal::SwitchDevice aDevice,
+ hal::SwitchState aState);
+
+/**
+ * Register an observer that is notified when a programmed alarm
+ * expires.
+ *
+ * Currently, there can only be 0 or 1 alarm observers.
+ */
+MOZ_MUST_USE bool RegisterTheOneAlarmObserver(hal::AlarmObserver* aObserver);
+
+/**
+ * Unregister the alarm observer. Doing so will implicitly cancel any
+ * programmed alarm.
+ */
+void UnregisterTheOneAlarmObserver();
+
+/**
+ * Notify that the programmed alarm has expired.
+ *
+ * This API is internal to hal; clients shouldn't call it directly.
+ */
+void NotifyAlarmFired();
+
+/**
+ * Program the real-time clock to expire at the time |aSeconds|,
+ * |aNanoseconds|. These specify a point in real time relative to the
+ * UNIX epoch. The alarm will fire at this time point even if the
+ * real-time clock is changed; that is, this alarm respects changes to
+ * the real-time clock. Return true iff the alarm was programmed.
+ *
+ * The alarm can be reprogrammed at any time.
+ *
+ * This API is currently only allowed to be used from non-sandboxed
+ * contexts.
+ */
+MOZ_MUST_USE bool SetAlarm(int32_t aSeconds, int32_t aNanoseconds);
+
+/**
+ * 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,
+ uint32_t aLRU = 0);
+
+
+/**
+ * Set the current thread's priority to appropriate platform-specific value for
+ * given functionality. Instead of providing arbitrary priority numbers you
+ * must specify a type of function like THREAD_PRIORITY_COMPOSITOR.
+ */
+void SetCurrentThreadPriority(hal::ThreadPriority aThreadPriority);
+
+/**
+ * Set a thread priority to appropriate platform-specific value for
+ * given functionality. Instead of providing arbitrary priority numbers you
+ * must specify a type of function like THREAD_PRIORITY_COMPOSITOR.
+ */
+void SetThreadPriority(PlatformThreadId aThreadId,
+ hal::ThreadPriority aThreadPriority);
+
+/**
+ * Start a watchdog to compulsively shutdown the system if it hangs.
+ * @param aMode Specify how to shutdown the system.
+ * @param aTimeoutSecs Specify the delayed seconds to shutdown the system.
+ *
+ * This API is currently only allowed to be used from the main process.
+ */
+void StartForceQuitWatchdog(hal::ShutdownMode aMode, int32_t aTimeoutSecs);
+
+/**
+ * Perform Factory Reset to wipe out all user data.
+ */
+void FactoryReset(mozilla::dom::FactoryResetReason& aReason);
+
+/**
+ * Start monitoring disk space for low space situations.
+ *
+ * This API is currently only allowed to be used from the main process.
+ */
+void StartDiskSpaceWatcher();
+
+/**
+ * Stop monitoring disk space for low space situations.
+ *
+ * This API is currently only allowed to be used from the main process.
+ */
+void StopDiskSpaceWatcher();
+
+/**
+ * Get total system memory of device being run on in bytes.
+ *
+ * Returns 0 if we are unable to determine this information from /proc/meminfo.
+ */
+uint32_t GetTotalSystemMemory();
+
+/**
+ * Determine whether the headphone switch event is from input device
+ */
+bool IsHeadphoneEventFromInputDev();
+
+/**
+ * Start the system service with the specified name and arguments.
+ */
+nsresult StartSystemService(const char* aSvcName, const char* aArgs);
+
+/**
+ * Stop the system service with the specified name.
+ */
+void StopSystemService(const char* aSvcName);
+
+/**
+ * Determine whether the system service with the specified name is running.
+ */
+bool SystemServiceIsRunning(const char* aSvcName);
+
+} // 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/HalImpl.h b/hal/HalImpl.h
new file mode 100644
index 000000000..9143cc5de
--- /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 000000000..d93023dd0
--- /dev/null
+++ b/hal/HalInternal.h
@@ -0,0 +1,105 @@
+/* -*- 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();
+
+/**
+ * Enable switch notifications from the backend
+ */
+void EnableSwitchNotifications(hal::SwitchDevice aDevice);
+
+/**
+ * Disable switch notifications from the backend
+ */
+void DisableSwitchNotifications(hal::SwitchDevice aDevice);
+
+/**
+ * Enable alarm notifications from the backend.
+ */
+MOZ_MUST_USE bool EnableAlarm();
+
+/**
+ * Disable alarm notifications from the backend.
+ */
+void DisableAlarm();
+
+/**
+ * Enable system clock change notifications from the backend.
+ */
+void EnableSystemClockChangeNotifications();
+
+/**
+ * Disable system clock change notifications from the backend.
+ */
+void DisableSystemClockChangeNotifications();
+
+/**
+ * Enable system timezone change notifications from the backend.
+ */
+void EnableSystemTimezoneChangeNotifications();
+
+/**
+ * Disable system timezone change notifications from the backend.
+ */
+void DisableSystemTimezoneChangeNotifications();
+
+/**
+ * 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 000000000..0d72e98d0
--- /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/HalSandbox.h b/hal/HalSandbox.h
new file mode 100644
index 000000000..255a1aae9
--- /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 000000000..dce84fd31
--- /dev/null
+++ b/hal/HalScreenConfiguration.h
@@ -0,0 +1,20 @@
+/* -*- 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"
+
+namespace mozilla {
+namespace hal {
+class ScreenConfiguration;
+typedef Observer<ScreenConfiguration> ScreenConfigurationObserver;
+} // namespace hal
+} // namespace mozilla
+
+#endif // mozilla_HalScreenConfiguration_h
+
diff --git a/hal/HalSensor.h b/hal/HalSensor.h
new file mode 100644
index 000000000..551c4271d
--- /dev/null
+++ b/hal/HalSensor.h
@@ -0,0 +1,80 @@
+/* -*- 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_UNKNOWN = -1,
+ 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;
+
+/**
+ * Enumeration of sensor accuracy types.
+ */
+enum SensorAccuracyType {
+ SENSOR_ACCURACY_UNKNOWN = -1,
+ SENSOR_ACCURACY_UNRELIABLE,
+ SENSOR_ACCURACY_LOW,
+ SENSOR_ACCURACY_MED,
+ SENSOR_ACCURACY_HIGH,
+ NUM_SENSOR_ACCURACY_TYPE
+};
+
+class SensorAccuracy;
+
+typedef Observer<SensorAccuracy> ISensorAccuracyObserver;
+
+} // namespace hal
+} // namespace mozilla
+
+#include "ipc/IPCMessageUtils.h"
+
+namespace IPC {
+ /**
+ * Serializer for SensorType
+ */
+ template <>
+ struct ParamTraits<mozilla::hal::SensorType>:
+ public ContiguousEnumSerializer<
+ mozilla::hal::SensorType,
+ mozilla::hal::SENSOR_UNKNOWN,
+ mozilla::hal::NUM_SENSOR_TYPE> {
+ };
+
+ template <>
+ struct ParamTraits<mozilla::hal::SensorAccuracyType>:
+ public ContiguousEnumSerializer<
+ mozilla::hal::SensorAccuracyType,
+ mozilla::hal::SENSOR_ACCURACY_UNKNOWN,
+ mozilla::hal::NUM_SENSOR_ACCURACY_TYPE> {
+
+ };
+} // namespace IPC
+
+#endif /* __HAL_SENSOR_H_ */
diff --git a/hal/HalTypes.h b/hal/HalTypes.h
new file mode 100644
index 000000000..5f0ad3ff7
--- /dev/null
+++ b/hal/HalTypes.h
@@ -0,0 +1,177 @@
+/* -*- 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/IPCMessageUtils.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;
+
+/**
+ * These are defined by libhardware, specifically, hardware/libhardware/include/hardware/lights.h
+ * in the gonk subsystem.
+ * If these change and are exposed to JS, make sure nsIHal.idl is updated as well.
+ */
+enum ShutdownMode {
+ eHalShutdownMode_Unknown = -1,
+ eHalShutdownMode_PowerOff = 0,
+ eHalShutdownMode_Reboot = 1,
+ eHalShutdownMode_Restart = 2,
+ eHalShutdownMode_Count = 3
+};
+
+class SwitchEvent;
+
+enum SwitchDevice {
+ SWITCH_DEVICE_UNKNOWN = -1,
+ SWITCH_HEADPHONES,
+ SWITCH_USB,
+ NUM_SWITCH_DEVICE
+};
+
+enum SwitchState {
+ SWITCH_STATE_UNKNOWN = -1,
+ SWITCH_STATE_ON,
+ SWITCH_STATE_OFF,
+ SWITCH_STATE_HEADSET, // Headphone with microphone
+ SWITCH_STATE_HEADPHONE, // without microphone
+ NUM_SWITCH_STATE
+};
+
+typedef Observer<SwitchEvent> SwitchObserver;
+
+// 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_MASTER,
+ NUM_PROCESS_PRIORITY
+};
+
+/**
+ * Values that can be passed to hal::SetCurrentThreadPriority(). These should be
+ * functional in nature, such as COMPOSITOR, instead of levels, like LOW/HIGH.
+ * This allows us to tune our priority scheme for the system in one place such
+ * that it makes sense holistically for the overall operating system. On gonk
+ * or android we may want different priority schemes than on windows, etc.
+ */
+enum ThreadPriority {
+ THREAD_PRIORITY_COMPOSITOR,
+ NUM_THREAD_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);
+
+/**
+ * Convert a ThreadPriority 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 assert in debug builds
+ * and otherwise return "???".
+ */
+const char *
+ThreadPriorityToString(ThreadPriority 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 {
+
+/**
+ * Serializer for ShutdownMode.
+ */
+template <>
+struct ParamTraits<mozilla::hal::ShutdownMode>
+ : public ContiguousEnumSerializer<
+ mozilla::hal::ShutdownMode,
+ mozilla::hal::eHalShutdownMode_Unknown,
+ mozilla::hal::eHalShutdownMode_Count>
+{};
+
+/**
+ * WakeLockControl serializer.
+ */
+template <>
+struct ParamTraits<mozilla::hal::WakeLockControl>
+ : public ContiguousEnumSerializer<
+ mozilla::hal::WakeLockControl,
+ mozilla::hal::WAKE_LOCK_REMOVE_ONE,
+ mozilla::hal::NUM_WAKE_LOCK>
+{};
+
+/**
+ * Serializer for SwitchState
+ */
+template <>
+struct ParamTraits<mozilla::hal::SwitchState>:
+ public ContiguousEnumSerializer<
+ mozilla::hal::SwitchState,
+ mozilla::hal::SWITCH_STATE_UNKNOWN,
+ mozilla::hal::NUM_SWITCH_STATE> {
+};
+
+/**
+ * Serializer for SwitchDevice
+ */
+template <>
+struct ParamTraits<mozilla::hal::SwitchDevice>:
+ public ContiguousEnumSerializer<
+ mozilla::hal::SwitchDevice,
+ mozilla::hal::SWITCH_DEVICE_UNKNOWN,
+ mozilla::hal::NUM_SWITCH_DEVICE> {
+};
+
+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 000000000..af4dc454e
--- /dev/null
+++ b/hal/HalWakeLock.cpp
@@ -0,0 +1,282 @@
+/* -*- 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 "mozilla/HalWakeLock.h"
+#include "mozilla/Services.h"
+#include "mozilla/StaticPtr.h"
+#include "mozilla/dom/ContentParent.h"
+#include "nsAutoPtr.h"
+#include "nsClassHashtable.h"
+#include "nsDataHashtable.h"
+#include "nsHashKeys.h"
+#include "nsIPropertyBag2.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;
+ nsTArray<uint64_t> processes;
+};
+
+typedef nsDataHashtable<nsUint64HashKey, LockCount> ProcessLockTable;
+typedef nsClassHashtable<nsStringHashKey, ProcessLockTable> LockTable;
+
+int sActiveListeners = 0;
+StaticAutoPtr<LockTable> sLockTable;
+bool sInitialized = false;
+bool sIsShuttingDown = false;
+
+WakeLockInformation
+WakeLockInfoFromLockCount(const nsAString& aTopic, const LockCount& aLockCount)
+{
+ // TODO: Once we abandon b2g18, we can switch this to use the
+ // WakeLockInformation constructor, which is better because it doesn't let us
+ // forget to assign a param. For now we have to do it this way, because
+ // b2g18 doesn't have the nsTArray <--> InfallibleTArray conversion (bug
+ // 819791).
+
+ WakeLockInformation info;
+ info.topic() = aTopic;
+ info.numLocks() = aLockCount.numLocks;
+ info.numHidden() = aLockCount.numHidden;
+ info.lockingProcesses().AppendElements(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(NS_LITERAL_STRING("childID"),
+ &childID);
+ if (NS_SUCCEEDED(rv)) {
+ for (auto iter = sLockTable->Iter(); !iter.Done(); iter.Next()) {
+ nsAutoPtr<ProcessLockTable>& table = iter.Data();
+
+ 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;
+}
+
+void
+Init()
+{
+ sLockTable = new LockTable();
+ sInitialized = true;
+
+ nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
+ if (obs) {
+ obs->AddObserver(new ClearHashtableOnShutdown(), "xpcom-shutdown", false);
+ obs->AddObserver(new CleanupOnContentShutdown(), "ipc:content-shutdown", false);
+ }
+}
+
+} // namespace
+
+namespace mozilla {
+
+namespace hal {
+
+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;
+ }
+ if (!sInitialized) {
+ Init();
+ }
+
+ ProcessLockTable* table = sLockTable->Get(aTopic);
+ LockCount processCount;
+ LockCount totalCount;
+ if (!table) {
+ table = new ProcessLockTable();
+ sLockTable->Put(aTopic, table);
+ } else {
+ table->Get(aProcessID, &processCount);
+ CountWakeLocks(table, &totalCount);
+ }
+
+ 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);
+ bool processWasLocked = processCount.numLocks > 0;
+
+ processCount.numLocks += aLockAdjust;
+ processCount.numHidden += aHiddenAdjust;
+
+ totalCount.numLocks += aLockAdjust;
+ totalCount.numHidden += aHiddenAdjust;
+
+ if (processCount.numLocks) {
+ table->Put(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 (!sInitialized) {
+ Init();
+ }
+
+ 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 000000000..7ee6327d8
--- /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/WindowIdentifier.cpp b/hal/WindowIdentifier.cpp
new file mode 100644
index 000000000..8e1354ba7
--- /dev/null
+++ b/hal/WindowIdentifier.cpp
@@ -0,0 +1,80 @@
+/* -*- 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)
+ , mIsEmpty(true)
+{
+}
+
+WindowIdentifier::WindowIdentifier(nsPIDOMWindowInner* window)
+ : mWindow(window)
+ , mIsEmpty(false)
+{
+ mID.AppendElement(GetWindowID());
+}
+
+WindowIdentifier::WindowIdentifier(const InfallibleTArray<uint64_t> &id,
+ nsPIDOMWindowInner* window)
+ : mID(id)
+ , mWindow(window)
+ , mIsEmpty(false)
+{
+ mID.AppendElement(GetWindowID());
+}
+
+WindowIdentifier::WindowIdentifier(const WindowIdentifier &other)
+ : mID(other.mID)
+ , mWindow(other.mWindow)
+ , mIsEmpty(other.mIsEmpty)
+{
+}
+
+const InfallibleTArray<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 000000000..928a16896
--- /dev/null
+++ b/hal/WindowIdentifier.h
@@ -0,0 +1,111 @@
+/* -*- 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();
+
+ /**
+ * Copy constructor.
+ */
+ WindowIdentifier(const WindowIdentifier& other);
+
+ /**
+ * 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(const InfallibleTArray<uint64_t>& id,
+ nsPIDOMWindowInner* window);
+
+ /**
+ * Get the list of window and process IDs we contain.
+ */
+ typedef InfallibleTArray<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;
+ bool mIsEmpty;
+};
+
+} // namespace hal
+} // namespace mozilla
+
+#endif // mozilla_hal_WindowIdentifier_h
diff --git a/hal/android/AndroidAlarm.cpp b/hal/android/AndroidAlarm.cpp
new file mode 100644
index 000000000..3b2613be8
--- /dev/null
+++ b/hal/android/AndroidAlarm.cpp
@@ -0,0 +1,48 @@
+/* -*- 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 "GeneratedJNINatives.h"
+#include "GeneratedJNIWrappers.h"
+
+using namespace mozilla::hal;
+
+namespace mozilla {
+
+class AlarmReceiver : public java::AlarmReceiver::Natives<AlarmReceiver>
+{
+private:
+ AlarmReceiver();
+
+public:
+ static void NotifyAlarmFired() {
+ hal::NotifyAlarmFired();
+ }
+};
+
+namespace hal_impl {
+
+bool
+EnableAlarm()
+{
+ AlarmReceiver::Init();
+ return true;
+}
+
+void
+DisableAlarm()
+{
+ java::GeckoAppShell::DisableAlarm();
+}
+
+bool
+SetAlarm(int32_t aSeconds, int32_t aNanoseconds)
+{
+ return java::GeckoAppShell::SetAlarm(aSeconds, aNanoseconds);
+}
+
+} // hal_impl
+} // mozilla
diff --git a/hal/android/AndroidHal.cpp b/hal/android/AndroidHal.cpp
new file mode 100644
index 000000000..495b623ef
--- /dev/null
+++ b/hal/android/AndroidHal.cpp
@@ -0,0 +1,175 @@
+/* -*- 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/dom/ScreenOrientation.h"
+#include "nsIScreenManager.h"
+#include "nsServiceManagerUtils.h"
+
+using namespace mozilla::dom;
+using namespace mozilla::hal;
+
+namespace java = mozilla::java;
+
+namespace mozilla {
+namespace hal_impl {
+
+void
+Vibrate(const nsTArray<uint32_t> &pattern, const 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(const 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);
+}
+
+void
+EnableScreenConfigurationNotifications()
+{
+ java::GeckoAppShell::EnableScreenOrientationNotifications();
+}
+
+void
+DisableScreenConfigurationNotifications()
+{
+ java::GeckoAppShell::DisableScreenOrientationNotifications();
+}
+
+void
+GetCurrentScreenConfiguration(ScreenConfiguration* aScreenConfiguration)
+{
+ AndroidBridge* bridge = AndroidBridge::Bridge();
+ if (!bridge) {
+ return;
+ }
+
+ nsresult rv;
+ nsCOMPtr<nsIScreenManager> screenMgr =
+ do_GetService("@mozilla.org/gfx/screenmanager;1", &rv);
+ if (NS_FAILED(rv)) {
+ NS_ERROR("Can't find nsIScreenManager!");
+ return;
+ }
+
+ nsIntRect rect;
+ int32_t colorDepth, pixelDepth;
+ int16_t angle;
+ ScreenOrientationInternal orientation;
+ nsCOMPtr<nsIScreen> screen;
+
+ screenMgr->GetPrimaryScreen(getter_AddRefs(screen));
+ screen->GetRect(&rect.x, &rect.y, &rect.width, &rect.height);
+ screen->GetColorDepth(&colorDepth);
+ screen->GetPixelDepth(&pixelDepth);
+ orientation = static_cast<ScreenOrientationInternal>(bridge->GetScreenOrientation());
+ angle = bridge->GetScreenAngle();
+
+ *aScreenConfiguration =
+ hal::ScreenConfiguration(rect, orientation, angle, colorDepth, pixelDepth);
+}
+
+bool
+LockScreenOrientation(const ScreenOrientationInternal& aOrientation)
+{
+ // Force the default orientation to be portrait-primary.
+ ScreenOrientationInternal orientation =
+ aOrientation == eScreenOrientation_Default ? eScreenOrientation_PortraitPrimary
+ : aOrientation;
+
+ switch (orientation) {
+ // The Android backend only supports these orientations.
+ case eScreenOrientation_PortraitPrimary:
+ case eScreenOrientation_PortraitSecondary:
+ case eScreenOrientation_PortraitPrimary | eScreenOrientation_PortraitSecondary:
+ case eScreenOrientation_LandscapePrimary:
+ case eScreenOrientation_LandscapeSecondary:
+ case eScreenOrientation_LandscapePrimary | eScreenOrientation_LandscapeSecondary:
+ case eScreenOrientation_Default:
+ java::GeckoAppShell::LockScreenOrientation(orientation);
+ return true;
+ default:
+ return false;
+ }
+}
+
+void
+UnlockScreenOrientation()
+{
+ java::GeckoAppShell::UnlockScreenOrientation();
+}
+
+} // hal_impl
+} // mozilla
+
diff --git a/hal/android/AndroidSensor.cpp b/hal/android/AndroidSensor.cpp
new file mode 100644
index 000000000..cb1060342
--- /dev/null
+++ b/hal/android/AndroidSensor.cpp
@@ -0,0 +1,25 @@
+/* -*- 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 "GeneratedJNIWrappers.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);
+}
+
+} // hal_impl
+} // mozilla
diff --git a/hal/cocoa/CocoaBattery.cpp b/hal/cocoa/CocoaBattery.cpp
new file mode 100644
index 000000000..6f1b7b1dc
--- /dev/null
+++ b/hal/cocoa/CocoaBattery.cpp
@@ -0,0 +1,325 @@
+/* -*- 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()
+{
+ MOZ_ASSERT(mRunLoopSource, "IOPS Notification Loop Source not set up. "
+ "(StopListening without BeginListening)");
+
+ ::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.
+ int currentCapacity = 0;
+ const void* cfRef = ::CFDictionaryGetValue(currPowerSourceDesc, CFSTR(kIOPSCurrentCapacityKey));
+ ::CFNumberGetValue((CFNumberRef)cfRef, kCFNumberSInt32Type, &currentCapacity);
+
+ // This key is also required.
+ int maxCapacity = 0;
+ cfRef = ::CFDictionaryGetValue(currPowerSourceDesc, CFSTR(kIOPSMaxCapacityKey));
+ ::CFNumberGetValue((CFNumberRef)cfRef, kCFNumberSInt32Type, &maxCapacity);
+
+ if (maxCapacity > 0) {
+ level = static_cast<double>(currentCapacity)/static_cast<double>(maxCapacity);
+ }
+
+ // Find out if we're charging.
+ // This key is optional, we fallback to kDefaultCharging if the current power
+ // source doesn't have that info.
+ if(::CFDictionaryGetValueIfPresent(currPowerSourceDesc, CFSTR(kIOPSIsChargingKey), &cfRef)) {
+ charging = ::CFBooleanGetValue((CFBooleanRef)cfRef);
+
+ // Get an estimate of how long it's going to take until we're fully charged.
+ // This key is optional.
+ if (charging) {
+ // Default value that will be changed if we happen to find the actual
+ // remaining time.
+ remainingTime = level == 1.0 ? kDefaultRemainingTime : kUnknownRemainingTime;
+
+ if (::CFDictionaryGetValueIfPresent(currPowerSourceDesc,
+ CFSTR(kIOPSTimeToFullChargeKey), &cfRef)) {
+ int timeToCharge;
+ ::CFNumberGetValue((CFNumberRef)cfRef, kCFNumberIntType, &timeToCharge);
+ if (timeToCharge != kIOPSTimeRemainingUnknown) {
+ remainingTime = timeToCharge*60;
+ }
+ }
+ } else if (sIOPSGetTimeRemainingEstimate) { // not charging
+ // See if we can get a time estimate.
+ CFTimeInterval estimate = sIOPSGetTimeRemainingEstimate();
+ if (estimate == kIOPSTimeRemainingUnlimited || estimate == kIOPSTimeRemainingUnknown) {
+ remainingTime = kUnknownRemainingTime;
+ } else {
+ remainingTime = estimate;
+ }
+ }
+ }
+
+ break;
+ }
+
+ bool isNewData = level != power->mLevel || charging != power->mCharging ||
+ remainingTime != power->mRemainingTime;
+
+ power->mRemainingTime = remainingTime;
+ power->mCharging = charging;
+ power->mLevel = level;
+
+ // Notify the observers if stuff changed.
+ if (power->mShouldNotify && isNewData) {
+ hal::NotifyBatteryChange(hal::BatteryInformation(power->mLevel,
+ power->mCharging,
+ power->mRemainingTime));
+ }
+
+ ::CFRelease(data);
+ ::CFRelease(list);
+}
+
+} // namespace hal_impl
+} // namespace mozilla
diff --git a/hal/cocoa/CocoaSensor.mm b/hal/cocoa/CocoaSensor.mm
new file mode 100644
index 000000000..ad203073c
--- /dev/null
+++ b/hal/cocoa/CocoaSensor.mm
@@ -0,0 +1,149 @@
+/* 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);
+ InfallibleTArray<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::SENSOR_ACCURACY_UNKNOWN);
+ 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->InitWithFuncCallback(UpdateHandler,
+ nullptr,
+ DEFAULT_SENSOR_POLL,
+ nsITimer::TYPE_REPEATING_SLACK);
+ }
+ }
+}
+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 000000000..2f0b2664e
--- /dev/null
+++ b/hal/cocoa/smslib.h
@@ -0,0 +1,159 @@
+/*
+ * 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 000000000..c11c1e4d6
--- /dev/null
+++ b/hal/cocoa/smslib.mm
@@ -0,0 +1,938 @@
+/*
+ * 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/FallbackAlarm.cpp b/hal/fallback/FallbackAlarm.cpp
new file mode 100644
index 000000000..9ec5c48e1
--- /dev/null
+++ b/hal/fallback/FallbackAlarm.cpp
@@ -0,0 +1,79 @@
+/* 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 <algorithm>
+
+#include "mozilla/ClearOnShutdown.h"
+#include "mozilla/StaticPtr.h"
+#include "nsComponentManagerUtils.h"
+#include "nsITimer.h"
+#include "nsThreadUtils.h"
+
+namespace mozilla {
+namespace hal_impl {
+
+static void
+TimerCallbackFunc(nsITimer *aTimer, void *aClosure)
+{
+ hal::NotifyAlarmFired();
+}
+
+static StaticRefPtr<nsITimer> sTimer;
+
+bool
+EnableAlarm()
+{
+ static bool initialized = false;
+ if (!initialized) {
+ initialized = true;
+ ClearOnShutdown(&sTimer);
+ }
+
+ nsCOMPtr<nsITimer> timer = do_CreateInstance("@mozilla.org/timer;1");
+ sTimer = timer;
+ MOZ_ASSERT(sTimer);
+ return true;
+}
+
+void
+DisableAlarm()
+{
+ /*
+ * DisableAlarm() may be called after sTimer has been set to null by
+ * ClearOnShutdown().
+ */
+ if (sTimer) {
+ sTimer->Cancel();
+ }
+}
+
+bool
+SetAlarm(int32_t aSeconds, int32_t aNanoseconds)
+{
+ if (!sTimer) {
+ MOZ_ASSERT(false, "We should have enabled the alarm");
+ return false;
+ }
+
+ // Do the math to convert aSeconds and aNanoseconds into milliseconds since
+ // the epoch.
+ int64_t milliseconds = static_cast<int64_t>(aSeconds) * 1000 +
+ static_cast<int64_t>(aNanoseconds) / 1000000;
+
+ // nsITimer expects relative milliseconds.
+ int64_t relMilliseconds = milliseconds - PR_Now() / 1000;
+
+ // If the alarm time is in the past relative to PR_Now(),
+ // we choose to immediately fire the alarm. Passing 0 means nsITimer will
+ // queue a timeout event immediately.
+ sTimer->InitWithFuncCallback(TimerCallbackFunc, nullptr,
+ clamped<int64_t>(relMilliseconds, 0, INT32_MAX),
+ nsITimer::TYPE_ONE_SHOT);
+ return true;
+}
+
+} // namespace hal_impl
+} // namespace mozilla
diff --git a/hal/fallback/FallbackBattery.cpp b/hal/fallback/FallbackBattery.cpp
new file mode 100644
index 000000000..3e5e71574
--- /dev/null
+++ b/hal/fallback/FallbackBattery.cpp
@@ -0,0 +1,30 @@
+/* -*- 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;
+}
+
+} // hal_impl
+} // namespace mozilla
diff --git a/hal/fallback/FallbackDiskSpaceWatcher.cpp b/hal/fallback/FallbackDiskSpaceWatcher.cpp
new file mode 100644
index 000000000..99d144397
--- /dev/null
+++ b/hal/fallback/FallbackDiskSpaceWatcher.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/. */
+
+namespace mozilla {
+namespace hal_impl {
+
+void
+StartDiskSpaceWatcher()
+{
+}
+
+void
+StopDiskSpaceWatcher()
+{
+}
+
+} // namespace hal_impl
+} // namespace mozilla
diff --git a/hal/fallback/FallbackFactoryReset.cpp b/hal/fallback/FallbackFactoryReset.cpp
new file mode 100644
index 000000000..76a727702
--- /dev/null
+++ b/hal/fallback/FallbackFactoryReset.cpp
@@ -0,0 +1,18 @@
+/* -*- 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"
+
+namespace mozilla {
+namespace hal_impl {
+
+void
+FactoryReset(mozilla::dom::FactoryResetReason&)
+{}
+
+} // namespace hal_impl
+} // namespace mozilla
diff --git a/hal/fallback/FallbackMemory.cpp b/hal/fallback/FallbackMemory.cpp
new file mode 100644
index 000000000..2750eebd8
--- /dev/null
+++ b/hal/fallback/FallbackMemory.cpp
@@ -0,0 +1,20 @@
+/* -*- 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"
+
+namespace mozilla {
+namespace hal_impl {
+
+uint32_t
+GetTotalSystemMemory()
+{
+ return 0;
+}
+
+} // namespace hal_impl
+} // namespace mozilla
diff --git a/hal/fallback/FallbackNetwork.cpp b/hal/fallback/FallbackNetwork.cpp
new file mode 100644
index 000000000..d8995bc6d
--- /dev/null
+++ b/hal/fallback/FallbackNetwork.cpp
@@ -0,0 +1,31 @@
+/* -*- 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/FallbackPower.cpp b/hal/fallback/FallbackPower.cpp
new file mode 100644
index 000000000..0a53e89ad
--- /dev/null
+++ b/hal/fallback/FallbackPower.cpp
@@ -0,0 +1,25 @@
+/* -*- 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"
+
+namespace mozilla {
+namespace hal_impl {
+
+void
+Reboot()
+{}
+
+void
+PowerOff()
+{}
+
+void
+StartForceQuitWatchdog(hal::ShutdownMode aMode, int32_t aTimeoutSecs)
+{}
+
+} // namespace hal_impl
+} // namespace mozilla
diff --git a/hal/fallback/FallbackProcessPriority.cpp b/hal/fallback/FallbackProcessPriority.cpp
new file mode 100644
index 000000000..b03b1542e
--- /dev/null
+++ b/hal/fallback/FallbackProcessPriority.cpp
@@ -0,0 +1,21 @@
+/* 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, uint32_t aLRU)
+{
+ HAL_LOG("FallbackProcessPriority - SetProcessPriority(%d, %s, %u)\n",
+ aPid, ProcessPriorityToString(aPriority), aLRU);
+}
+
+} // namespace hal_impl
+} // namespace mozilla
diff --git a/hal/fallback/FallbackScreenConfiguration.cpp b/hal/fallback/FallbackScreenConfiguration.cpp
new file mode 100644
index 000000000..cbee955fb
--- /dev/null
+++ b/hal/fallback/FallbackScreenConfiguration.cpp
@@ -0,0 +1,63 @@
+/* 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/ScreenOrientation.h"
+#include "nsIScreenManager.h"
+#include "nsServiceManagerUtils.h"
+
+namespace mozilla {
+namespace hal_impl {
+
+void
+EnableScreenConfigurationNotifications()
+{
+}
+
+void
+DisableScreenConfigurationNotifications()
+{
+}
+
+void
+GetCurrentScreenConfiguration(hal::ScreenConfiguration* aScreenConfiguration)
+{
+ nsresult rv;
+ nsCOMPtr<nsIScreenManager> screenMgr =
+ do_GetService("@mozilla.org/gfx/screenmanager;1", &rv);
+ if (NS_FAILED(rv)) {
+ NS_ERROR("Can't find nsIScreenManager!");
+ return;
+ }
+
+ nsIntRect rect;
+ int32_t colorDepth, pixelDepth;
+ dom::ScreenOrientationInternal orientation;
+ nsCOMPtr<nsIScreen> screen;
+
+ screenMgr->GetPrimaryScreen(getter_AddRefs(screen));
+ screen->GetRect(&rect.x, &rect.y, &rect.width, &rect.height);
+ screen->GetColorDepth(&colorDepth);
+ screen->GetPixelDepth(&pixelDepth);
+ orientation = rect.width >= rect.height
+ ? dom::eScreenOrientation_LandscapePrimary
+ : dom::eScreenOrientation_PortraitPrimary;
+
+ *aScreenConfiguration =
+ hal::ScreenConfiguration(rect, orientation, 0, colorDepth, pixelDepth);
+}
+
+bool
+LockScreenOrientation(const dom::ScreenOrientationInternal& aOrientation)
+{
+ return false;
+}
+
+void
+UnlockScreenOrientation()
+{
+}
+
+} // namespace hal_impl
+} // namespace mozilla
diff --git a/hal/fallback/FallbackScreenPower.cpp b/hal/fallback/FallbackScreenPower.cpp
new file mode 100644
index 000000000..2a7ad276f
--- /dev/null
+++ b/hal/fallback/FallbackScreenPower.cpp
@@ -0,0 +1,41 @@
+/* -*- 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/. */
+
+namespace mozilla {
+namespace hal_impl {
+
+bool
+GetScreenEnabled()
+{
+ return true;
+}
+
+void
+SetScreenEnabled(bool aEnabled)
+{}
+
+bool
+GetKeyLightEnabled()
+{
+ return true;
+}
+
+void
+SetKeyLightEnabled(bool aEnabled)
+{}
+
+double
+GetScreenBrightness()
+{
+ return 1;
+}
+
+void
+SetScreenBrightness(double aBrightness)
+{}
+
+} // namespace hal_impl
+} // namespace mozilla
diff --git a/hal/fallback/FallbackSensor.cpp b/hal/fallback/FallbackSensor.cpp
new file mode 100644
index 000000000..22b1a0436
--- /dev/null
+++ b/hal/fallback/FallbackSensor.cpp
@@ -0,0 +1,23 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim: set sw=4 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) {
+}
+
+}
+}
diff --git a/hal/fallback/FallbackSwitch.cpp b/hal/fallback/FallbackSwitch.cpp
new file mode 100644
index 000000000..e9b7eab0a
--- /dev/null
+++ b/hal/fallback/FallbackSwitch.cpp
@@ -0,0 +1,39 @@
+/* -*- 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 "mozilla/Hal.h"
+
+using namespace mozilla::hal;
+
+namespace mozilla {
+namespace hal_impl {
+
+void
+EnableSwitchNotifications(SwitchDevice aDevice)
+{
+}
+
+void
+DisableSwitchNotifications(SwitchDevice aDevice)
+{
+}
+
+SwitchState
+GetCurrentSwitchState(SwitchDevice aDevice) {
+ return SWITCH_STATE_UNKNOWN;
+}
+
+void
+NotifySwitchStateFromInputDevice(SwitchDevice aDevice, SwitchState aState)
+{
+}
+
+bool IsHeadphoneEventFromInputDev()
+{
+ return false;
+}
+
+} // namespace hal_impl
+} // namespace mozilla
diff --git a/hal/fallback/FallbackSystemService.cpp b/hal/fallback/FallbackSystemService.cpp
new file mode 100644
index 000000000..93365c47c
--- /dev/null
+++ b/hal/fallback/FallbackSystemService.cpp
@@ -0,0 +1,35 @@
+/* -*- 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"
+
+namespace mozilla {
+namespace hal_impl {
+
+nsresult
+StartSystemService(const char* aSvcName, const char* aArgs)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+void
+StopSystemService(const char* aSvcName)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+}
+
+bool
+SystemServiceIsRunning(const char* aSvcName)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+
+ return false;
+}
+
+} // namespace hal_impl
+} // namespace mozilla
diff --git a/hal/fallback/FallbackThreadPriority.cpp b/hal/fallback/FallbackThreadPriority.cpp
new file mode 100644
index 000000000..dce5dfa82
--- /dev/null
+++ b/hal/fallback/FallbackThreadPriority.cpp
@@ -0,0 +1,29 @@
+/* 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
+SetCurrentThreadPriority(ThreadPriority aPriority)
+{
+ HAL_LOG("FallbackThreadPriority - SetCurrentThreadPriority(%d)\n",
+ ThreadPriorityToString(aPriority));
+}
+
+void
+SetThreadPriority(PlatformThreadId aThreadId,
+ ThreadPriority aPriority)
+{
+ HAL_LOG("FallbackThreadPriority - SetThreadPriority(%d, %d)\n",
+ aThreadId, ThreadPriorityToString(aPriority));
+}
+
+} // namespace hal_impl
+} // namespace mozilla
diff --git a/hal/fallback/FallbackTime.cpp b/hal/fallback/FallbackTime.cpp
new file mode 100644
index 000000000..a2c56e817
--- /dev/null
+++ b/hal/fallback/FallbackTime.cpp
@@ -0,0 +1,54 @@
+/* -*- 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"
+
+namespace mozilla {
+namespace hal_impl {
+
+void
+AdjustSystemClock(int64_t aDeltaMilliseconds)
+{}
+
+void
+SetTimezone(const nsCString& aTimezoneSpec)
+{}
+
+nsCString
+GetTimezone()
+{
+ return EmptyCString();
+}
+
+int32_t
+GetTimezoneOffset()
+{
+ return 0;
+}
+
+void
+EnableSystemClockChangeNotifications()
+{
+}
+
+void
+DisableSystemClockChangeNotifications()
+{
+}
+
+void
+EnableSystemTimezoneChangeNotifications()
+{
+}
+
+void
+DisableSystemTimezoneChangeNotifications()
+{
+}
+
+} // namespace hal_impl
+} // namespace mozilla
diff --git a/hal/fallback/FallbackVibration.cpp b/hal/fallback/FallbackVibration.cpp
new file mode 100644
index 000000000..ffabe9efe
--- /dev/null
+++ b/hal/fallback/FallbackVibration.cpp
@@ -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/. */
+
+#include "Hal.h"
+
+using mozilla::hal::WindowIdentifier;
+
+namespace mozilla {
+namespace hal_impl {
+
+void
+Vibrate(const nsTArray<uint32_t>& pattern, const hal::WindowIdentifier &)
+{}
+
+void
+CancelVibrate(const hal::WindowIdentifier &)
+{}
+
+} // namespace hal_impl
+} // namespace mozilla
diff --git a/hal/fallback/FallbackWakeLocks.cpp b/hal/fallback/FallbackWakeLocks.cpp
new file mode 100644
index 000000000..1fd97a2e6
--- /dev/null
+++ b/hal/fallback/FallbackWakeLocks.cpp
@@ -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/. */
+
+#include "Hal.h"
+
+namespace mozilla {
+namespace hal_impl {
+
+bool
+GetCpuSleepAllowed()
+{
+ return true;
+}
+
+void
+SetCpuSleepAllowed(bool allowed)
+{}
+
+} // namespace hal_impl
+} // namespace mozilla
diff --git a/hal/gonk/GonkDiskSpaceWatcher.cpp b/hal/gonk/GonkDiskSpaceWatcher.cpp
new file mode 100644
index 000000000..cdc48ef89
--- /dev/null
+++ b/hal/gonk/GonkDiskSpaceWatcher.cpp
@@ -0,0 +1,324 @@
+/* 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 <sys/syscall.h>
+#include <sys/vfs.h>
+#include <fcntl.h>
+#include <errno.h>
+#include "base/message_loop.h"
+#include "base/task.h"
+#include "DiskSpaceWatcher.h"
+#include "fanotify.h"
+#include "nsIObserverService.h"
+#include "nsIDiskSpaceWatcher.h"
+#include "nsThreadUtils.h"
+#include "nsXULAppAPI.h"
+#include "mozilla/ModuleUtils.h"
+#include "mozilla/Preferences.h"
+#include "mozilla/Services.h"
+
+using namespace mozilla;
+
+namespace mozilla { namespace hal_impl { class GonkDiskSpaceWatcher; } }
+
+using namespace mozilla::hal_impl;
+
+namespace mozilla {
+namespace hal_impl {
+
+// NOTE: this should be unnecessary once we no longer support ICS.
+#ifndef __NR_fanotify_init
+#if defined(__ARM_EABI__)
+#define __NR_fanotify_init 367
+#define __NR_fanotify_mark 368
+#elif defined(__i386__)
+#define __NR_fanotify_init 338
+#define __NR_fanotify_mark 339
+#else
+#error "Unhandled architecture"
+#endif
+#endif
+
+// fanotify_init and fanotify_mark functions are syscalls.
+// The user space bits are not part of bionic so we add them here
+// as well as fanotify.h
+int fanotify_init (unsigned int flags, unsigned int event_f_flags)
+{
+ return syscall(__NR_fanotify_init, flags, event_f_flags);
+}
+
+// Add, remove, or modify an fanotify mark on a filesystem object.
+int fanotify_mark (int fanotify_fd, unsigned int flags,
+ uint64_t mask, int dfd, const char *pathname)
+{
+
+ // On 32 bits platforms we have to convert the 64 bits mask into
+ // two 32 bits ints.
+ if (sizeof(void *) == 4) {
+ union {
+ uint64_t _64;
+ uint32_t _32[2];
+ } _mask;
+ _mask._64 = mask;
+ return syscall(__NR_fanotify_mark, fanotify_fd, flags,
+ _mask._32[0], _mask._32[1], dfd, pathname);
+ }
+
+ return syscall(__NR_fanotify_mark, fanotify_fd, flags, mask, dfd, pathname);
+}
+
+class GonkDiskSpaceWatcher final : public MessageLoopForIO::Watcher
+{
+public:
+ GonkDiskSpaceWatcher();
+ ~GonkDiskSpaceWatcher() {};
+
+ virtual void OnFileCanReadWithoutBlocking(int aFd);
+
+ // We should never write to the fanotify fd.
+ virtual void OnFileCanWriteWithoutBlocking(int aFd)
+ {
+ MOZ_CRASH("Must not write to fanotify fd");
+ }
+
+ void DoStart();
+ void DoStop();
+
+private:
+ void NotifyUpdate();
+
+ uint64_t mLowThreshold;
+ uint64_t mHighThreshold;
+ TimeDuration mTimeout;
+ TimeStamp mLastTimestamp;
+ uint64_t mLastFreeSpace;
+ uint32_t mSizeDelta;
+
+ bool mIsDiskFull;
+ uint64_t mFreeSpace;
+
+ int mFd;
+ MessageLoopForIO::FileDescriptorWatcher mReadWatcher;
+};
+
+static GonkDiskSpaceWatcher* gHalDiskSpaceWatcher = nullptr;
+
+#define WATCHER_PREF_LOW "disk_space_watcher.low_threshold"
+#define WATCHER_PREF_HIGH "disk_space_watcher.high_threshold"
+#define WATCHER_PREF_TIMEOUT "disk_space_watcher.timeout"
+#define WATCHER_PREF_SIZE_DELTA "disk_space_watcher.size_delta"
+
+static const char kWatchedPath[] = "/data";
+
+// Helper class to dispatch calls to xpcom on the main thread.
+class DiskSpaceNotifier : public Runnable
+{
+public:
+ DiskSpaceNotifier(const bool aIsDiskFull, const uint64_t aFreeSpace) :
+ mIsDiskFull(aIsDiskFull),
+ mFreeSpace(aFreeSpace) {}
+
+ NS_IMETHOD Run() override
+ {
+ MOZ_ASSERT(NS_IsMainThread());
+ DiskSpaceWatcher::UpdateState(mIsDiskFull, mFreeSpace);
+ return NS_OK;
+ }
+
+private:
+ bool mIsDiskFull;
+ uint64_t mFreeSpace;
+};
+
+// Helper runnable to delete the watcher on the main thread.
+class DiskSpaceCleaner : public Runnable
+{
+public:
+ NS_IMETHOD Run() override
+ {
+ MOZ_ASSERT(NS_IsMainThread());
+ if (gHalDiskSpaceWatcher) {
+ delete gHalDiskSpaceWatcher;
+ gHalDiskSpaceWatcher = nullptr;
+ }
+ return NS_OK;
+ }
+};
+
+GonkDiskSpaceWatcher::GonkDiskSpaceWatcher() :
+ mLastFreeSpace(UINT64_MAX),
+ mIsDiskFull(false),
+ mFreeSpace(UINT64_MAX),
+ mFd(-1)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(gHalDiskSpaceWatcher == nullptr);
+
+ // Default values: 5MB for low threshold, 10MB for high threshold, and
+ // a timeout of 5 seconds.
+ mLowThreshold = Preferences::GetInt(WATCHER_PREF_LOW, 5) * 1024 * 1024;
+ mHighThreshold = Preferences::GetInt(WATCHER_PREF_HIGH, 10) * 1024 * 1024;
+ mTimeout = TimeDuration::FromSeconds(Preferences::GetInt(WATCHER_PREF_TIMEOUT, 5));
+ mSizeDelta = Preferences::GetInt(WATCHER_PREF_SIZE_DELTA, 1) * 1024 * 1024;
+}
+
+void
+GonkDiskSpaceWatcher::DoStart()
+{
+ NS_ASSERTION(XRE_GetIOMessageLoop() == MessageLoopForIO::current(),
+ "Not on the correct message loop");
+
+ mFd = fanotify_init(FAN_CLASS_NOTIF, FAN_CLOEXEC | O_LARGEFILE);
+ if (mFd == -1) {
+ if (errno == ENOSYS) {
+ // Don't change these printf_stderr since we need these logs even
+ // in opt builds.
+ printf_stderr("Warning: No fanotify support in this device's kernel.\n");
+#if ANDROID_VERSION >= 19
+ MOZ_CRASH("Fanotify support must be enabled in the kernel.");
+#endif
+ } else {
+ printf_stderr("Error calling fanotify_init()");
+ }
+ return;
+ }
+
+ if (fanotify_mark(mFd, FAN_MARK_ADD | FAN_MARK_MOUNT, FAN_CLOSE,
+ 0, kWatchedPath) < 0) {
+ NS_WARNING("Error calling fanotify_mark");
+ close(mFd);
+ mFd = -1;
+ return;
+ }
+
+ if (!MessageLoopForIO::current()->WatchFileDescriptor(
+ mFd, /* persistent = */ true,
+ MessageLoopForIO::WATCH_READ,
+ &mReadWatcher, gHalDiskSpaceWatcher)) {
+ NS_WARNING("Unable to watch fanotify fd.");
+ close(mFd);
+ mFd = -1;
+ }
+}
+
+void
+GonkDiskSpaceWatcher::DoStop()
+{
+ NS_ASSERTION(XRE_GetIOMessageLoop() == MessageLoopForIO::current(),
+ "Not on the correct message loop");
+
+ if (mFd != -1) {
+ mReadWatcher.StopWatchingFileDescriptor();
+ fanotify_mark(mFd, FAN_MARK_FLUSH, 0, 0, kWatchedPath);
+ close(mFd);
+ mFd = -1;
+ }
+
+ // Dispatch the cleanup to the main thread.
+ nsCOMPtr<nsIRunnable> runnable = new DiskSpaceCleaner();
+ NS_DispatchToMainThread(runnable);
+}
+
+// We are called off the main thread, so we proxy first to the main thread
+// before calling the xpcom object.
+void
+GonkDiskSpaceWatcher::NotifyUpdate()
+{
+ mLastTimestamp = TimeStamp::Now();
+ mLastFreeSpace = mFreeSpace;
+
+ nsCOMPtr<nsIRunnable> runnable =
+ new DiskSpaceNotifier(mIsDiskFull, mFreeSpace);
+ NS_DispatchToMainThread(runnable);
+}
+
+void
+GonkDiskSpaceWatcher::OnFileCanReadWithoutBlocking(int aFd)
+{
+ struct fanotify_event_metadata* fem = nullptr;
+ char buf[4096];
+ struct statfs sfs;
+ int32_t len, rc;
+
+ do {
+ len = read(aFd, buf, sizeof(buf));
+ } while(len == -1 && errno == EINTR);
+
+ // Bail out if the file is busy.
+ if (len < 0 && errno == ETXTBSY) {
+ return;
+ }
+
+ // We should get an exact multiple of fanotify_event_metadata
+ if (len <= 0 || (len % FAN_EVENT_METADATA_LEN != 0)) {
+ MOZ_CRASH("About to crash: fanotify_event_metadata read error.");
+ }
+
+ fem = reinterpret_cast<fanotify_event_metadata *>(buf);
+
+ while (FAN_EVENT_OK(fem, len)) {
+ rc = fstatfs(fem->fd, &sfs);
+ if (rc < 0) {
+ NS_WARNING("Unable to stat fan_notify fd");
+ } else {
+ bool firstRun = mFreeSpace == UINT64_MAX;
+ mFreeSpace = sfs.f_bavail * sfs.f_bsize;
+ // We change from full <-> free depending on the free space and the
+ // low and high thresholds.
+ // Once we are in 'full' mode we send updates for all size changes with
+ // a minimum of time between messages or when we cross a size change
+ // threshold.
+ if (firstRun) {
+ mIsDiskFull = mFreeSpace <= mLowThreshold;
+ // Always notify the current state at first run.
+ NotifyUpdate();
+ } else if (!mIsDiskFull && (mFreeSpace <= mLowThreshold)) {
+ mIsDiskFull = true;
+ NotifyUpdate();
+ } else if (mIsDiskFull && (mFreeSpace > mHighThreshold)) {
+ mIsDiskFull = false;
+ NotifyUpdate();
+ } else if (mIsDiskFull) {
+ if (mTimeout < TimeStamp::Now() - mLastTimestamp ||
+ mSizeDelta < llabs(mFreeSpace - mLastFreeSpace)) {
+ NotifyUpdate();
+ }
+ }
+ }
+ close(fem->fd);
+ fem = FAN_EVENT_NEXT(fem, len);
+ }
+}
+
+void
+StartDiskSpaceWatcher()
+{
+ MOZ_ASSERT(NS_IsMainThread());
+
+ // Bail out if called several times.
+ if (gHalDiskSpaceWatcher != nullptr) {
+ return;
+ }
+
+ gHalDiskSpaceWatcher = new GonkDiskSpaceWatcher();
+
+ XRE_GetIOMessageLoop()->PostTask(
+ NewNonOwningRunnableMethod(gHalDiskSpaceWatcher, &GonkDiskSpaceWatcher::DoStart));
+}
+
+void
+StopDiskSpaceWatcher()
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ if (!gHalDiskSpaceWatcher) {
+ return;
+ }
+
+ XRE_GetIOMessageLoop()->PostTask(
+ NewNonOwningRunnableMethod(gHalDiskSpaceWatcher, &GonkDiskSpaceWatcher::DoStop));
+}
+
+} // namespace hal_impl
+} // namespace mozilla
diff --git a/hal/gonk/GonkHal.cpp b/hal/gonk/GonkHal.cpp
new file mode 100644
index 000000000..05d9295a2
--- /dev/null
+++ b/hal/gonk/GonkHal.cpp
@@ -0,0 +1,2045 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et ft=cpp : */
+/* Copyright 2012 Mozilla Foundation and Mozilla contributors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <linux/android_alarm.h>
+#include <math.h>
+#include <regex.h>
+#include <sched.h>
+#include <stdio.h>
+#include <sys/klog.h>
+#include <sys/stat.h>
+#include <sys/syscall.h>
+#include <sys/resource.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "mozilla/DebugOnly.h"
+
+#include "android/log.h"
+#include "cutils/properties.h"
+#include "hardware/hardware.h"
+#include "hardware/lights.h"
+#include "hardware_legacy/uevent.h"
+#include "hardware_legacy/vibrator.h"
+#include "hardware_legacy/power.h"
+#include "libdisplay/GonkDisplay.h"
+#include "utils/threads.h"
+
+#include "base/message_loop.h"
+#include "base/task.h"
+
+#include "Hal.h"
+#include "HalImpl.h"
+#include "HalLog.h"
+#include "mozilla/ArrayUtils.h"
+#include "mozilla/ClearOnShutdown.h"
+#include "mozilla/dom/battery/Constants.h"
+#include "mozilla/DebugOnly.h"
+#include "mozilla/FileUtils.h"
+#include "mozilla/Monitor.h"
+#include "mozilla/RefPtr.h"
+#include "mozilla/Services.h"
+#include "mozilla/StaticMutex.h"
+#include "mozilla/StaticPtr.h"
+#include "mozilla/Preferences.h"
+#include "mozilla/UniquePtrExtensions.h"
+#include "nsAlgorithm.h"
+#include "nsPrintfCString.h"
+#include "nsIObserver.h"
+#include "nsIObserverService.h"
+#include "nsIRecoveryService.h"
+#include "nsIRunnable.h"
+#include "nsScreenManagerGonk.h"
+#include "nsThreadUtils.h"
+#include "nsThreadUtils.h"
+#include "nsIThread.h"
+#include "nsXULAppAPI.h"
+#include "OrientationObserver.h"
+#include "UeventPoller.h"
+#include "nsIWritablePropertyBag2.h"
+#include <algorithm>
+
+#define NsecPerMsec 1000000LL
+#define NsecPerSec 1000000000
+
+// The header linux/oom.h is not available in bionic libc. We
+// redefine some of its constants here.
+
+#ifndef OOM_DISABLE
+#define OOM_DISABLE (-17)
+#endif
+
+#ifndef OOM_ADJUST_MIN
+#define OOM_ADJUST_MIN (-16)
+#endif
+
+#ifndef OOM_ADJUST_MAX
+#define OOM_ADJUST_MAX 15
+#endif
+
+#ifndef OOM_SCORE_ADJ_MIN
+#define OOM_SCORE_ADJ_MIN (-1000)
+#endif
+
+#ifndef OOM_SCORE_ADJ_MAX
+#define OOM_SCORE_ADJ_MAX 1000
+#endif
+
+#ifndef BATTERY_CHARGING_ARGB
+#define BATTERY_CHARGING_ARGB 0x00FF0000
+#endif
+#ifndef BATTERY_FULL_ARGB
+#define BATTERY_FULL_ARGB 0x0000FF00
+#endif
+
+using namespace mozilla;
+using namespace mozilla::hal;
+using namespace mozilla::dom;
+
+namespace mozilla {
+namespace hal_impl {
+
+/**
+ * These are defined by libhardware, specifically, hardware/libhardware/include/hardware/lights.h
+ * in the gonk subsystem.
+ * If these change and are exposed to JS, make sure nsIHal.idl is updated as well.
+ */
+enum LightType {
+ eHalLightID_Backlight = 0,
+ eHalLightID_Keyboard = 1,
+ eHalLightID_Buttons = 2,
+ eHalLightID_Battery = 3,
+ eHalLightID_Notifications = 4,
+ eHalLightID_Attention = 5,
+ eHalLightID_Bluetooth = 6,
+ eHalLightID_Wifi = 7,
+ eHalLightID_Count // This should stay at the end
+};
+enum LightMode {
+ eHalLightMode_User = 0, // brightness is managed by user setting
+ eHalLightMode_Sensor = 1, // brightness is managed by a light sensor
+ eHalLightMode_Count
+};
+enum FlashMode {
+ eHalLightFlash_None = 0,
+ eHalLightFlash_Timed = 1, // timed flashing. Use flashOnMS and flashOffMS for timing
+ eHalLightFlash_Hardware = 2, // hardware assisted flashing
+ eHalLightFlash_Count
+};
+
+struct LightConfiguration {
+ LightType light;
+ LightMode mode;
+ FlashMode flash;
+ uint32_t flashOnMS;
+ uint32_t flashOffMS;
+ uint32_t color;
+};
+
+static light_device_t* sLights[eHalLightID_Count]; // will be initialized to nullptr
+
+static light_device_t*
+GetDevice(hw_module_t* module, char const* name)
+{
+ int err;
+ hw_device_t* device;
+ err = module->methods->open(module, name, &device);
+ if (err == 0) {
+ return (light_device_t*)device;
+ } else {
+ return nullptr;
+ }
+}
+
+static void
+InitLights()
+{
+ // assume that if backlight is nullptr, nothing has been set yet
+ // if this is not true, the initialization will occur everytime a light is read or set!
+ if (!sLights[eHalLightID_Backlight]) {
+ int err;
+ hw_module_t* module;
+
+ err = hw_get_module(LIGHTS_HARDWARE_MODULE_ID, (hw_module_t const**)&module);
+ if (err == 0) {
+ sLights[eHalLightID_Backlight]
+ = GetDevice(module, LIGHT_ID_BACKLIGHT);
+ sLights[eHalLightID_Keyboard]
+ = GetDevice(module, LIGHT_ID_KEYBOARD);
+ sLights[eHalLightID_Buttons]
+ = GetDevice(module, LIGHT_ID_BUTTONS);
+ sLights[eHalLightID_Battery]
+ = GetDevice(module, LIGHT_ID_BATTERY);
+ sLights[eHalLightID_Notifications]
+ = GetDevice(module, LIGHT_ID_NOTIFICATIONS);
+ sLights[eHalLightID_Attention]
+ = GetDevice(module, LIGHT_ID_ATTENTION);
+ sLights[eHalLightID_Bluetooth]
+ = GetDevice(module, LIGHT_ID_BLUETOOTH);
+ sLights[eHalLightID_Wifi]
+ = GetDevice(module, LIGHT_ID_WIFI);
+ }
+ }
+}
+
+/**
+ * The state last set for the lights until liblights supports
+ * getting the light state.
+ */
+static light_state_t sStoredLightState[eHalLightID_Count];
+
+/**
+* Set the value of a light to a particular color, with a specific flash pattern.
+* light specifices which light. See Hal.idl for the list of constants
+* mode specifies user set or based on ambient light sensor
+* flash specifies whether or how to flash the light
+* flashOnMS and flashOffMS specify the pattern for XXX flash mode
+* color specifies the color. If the light doesn't support color, the given color is
+* transformed into a brightness, or just an on/off if that is all the light is capable of.
+* returns true if successful and false if failed.
+*/
+static bool
+SetLight(LightType light, const LightConfiguration& aConfig)
+{
+ light_state_t state;
+
+ InitLights();
+
+ if (light < 0 || light >= eHalLightID_Count ||
+ sLights[light] == nullptr) {
+ return false;
+ }
+
+ memset(&state, 0, sizeof(light_state_t));
+ state.color = aConfig.color;
+ state.flashMode = aConfig.flash;
+ state.flashOnMS = aConfig.flashOnMS;
+ state.flashOffMS = aConfig.flashOffMS;
+ state.brightnessMode = aConfig.mode;
+
+ sLights[light]->set_light(sLights[light], &state);
+ sStoredLightState[light] = state;
+ return true;
+}
+
+/**
+* GET the value of a light returning a particular color, with a specific flash pattern.
+* returns true if successful and false if failed.
+*/
+static bool
+GetLight(LightType light, LightConfiguration* aConfig)
+{
+ light_state_t state;
+
+ if (light < 0 || light >= eHalLightID_Count ||
+ sLights[light] == nullptr) {
+ return false;
+ }
+
+ memset(&state, 0, sizeof(light_state_t));
+ state = sStoredLightState[light];
+
+ aConfig->light = light;
+ aConfig->color = state.color;
+ aConfig->flash = FlashMode(state.flashMode);
+ aConfig->flashOnMS = state.flashOnMS;
+ aConfig->flashOffMS = state.flashOffMS;
+ aConfig->mode = LightMode(state.brightnessMode);
+
+ return true;
+}
+
+namespace {
+
+/**
+ * This runnable runs for the lifetime of the program, once started. It's
+ * responsible for "playing" vibration patterns.
+ */
+class VibratorRunnable final
+ : public nsIRunnable
+ , public nsIObserver
+{
+public:
+ VibratorRunnable()
+ : mMonitor("VibratorRunnable")
+ , mIndex(0)
+ {
+ nsCOMPtr<nsIObserverService> os = services::GetObserverService();
+ if (!os) {
+ NS_WARNING("Could not get observer service!");
+ return;
+ }
+
+ os->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
+ }
+
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSIRUNNABLE
+ NS_DECL_NSIOBSERVER
+
+ // Run on the main thread, not the vibrator thread.
+ void Vibrate(const nsTArray<uint32_t> &pattern);
+ void CancelVibrate();
+
+ static bool ShuttingDown() { return sShuttingDown; }
+
+protected:
+ ~VibratorRunnable() {}
+
+private:
+ Monitor mMonitor;
+
+ // The currently-playing pattern.
+ nsTArray<uint32_t> mPattern;
+
+ // The index we're at in the currently-playing pattern. If mIndex >=
+ // mPattern.Length(), then we're not currently playing anything.
+ uint32_t mIndex;
+
+ // Set to true in our shutdown observer. When this is true, we kill the
+ // vibrator thread.
+ static bool sShuttingDown;
+};
+
+NS_IMPL_ISUPPORTS(VibratorRunnable, nsIRunnable, nsIObserver);
+
+bool VibratorRunnable::sShuttingDown = false;
+
+static StaticRefPtr<VibratorRunnable> sVibratorRunnable;
+
+NS_IMETHODIMP
+VibratorRunnable::Run()
+{
+ MonitorAutoLock lock(mMonitor);
+
+ // We currently assume that mMonitor.Wait(X) waits for X milliseconds. But in
+ // reality, the kernel might not switch to this thread for some time after the
+ // wait expires. So there's potential for some inaccuracy here.
+ //
+ // This doesn't worry me too much. Note that we don't even start vibrating
+ // immediately when VibratorRunnable::Vibrate is called -- we go through a
+ // condvar onto another thread. Better just to be chill about small errors in
+ // the timing here.
+
+ while (!sShuttingDown) {
+ if (mIndex < mPattern.Length()) {
+ uint32_t duration = mPattern[mIndex];
+ if (mIndex % 2 == 0) {
+ vibrator_on(duration);
+ }
+ mIndex++;
+ mMonitor.Wait(PR_MillisecondsToInterval(duration));
+ }
+ else {
+ mMonitor.Wait();
+ }
+ }
+ sVibratorRunnable = nullptr;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+VibratorRunnable::Observe(nsISupports *subject, const char *topic,
+ const char16_t *data)
+{
+ MOZ_ASSERT(strcmp(topic, NS_XPCOM_SHUTDOWN_OBSERVER_ID) == 0);
+ MonitorAutoLock lock(mMonitor);
+ sShuttingDown = true;
+ mMonitor.Notify();
+
+ return NS_OK;
+}
+
+void
+VibratorRunnable::Vibrate(const nsTArray<uint32_t> &pattern)
+{
+ MonitorAutoLock lock(mMonitor);
+ mPattern = pattern;
+ mIndex = 0;
+ mMonitor.Notify();
+}
+
+void
+VibratorRunnable::CancelVibrate()
+{
+ MonitorAutoLock lock(mMonitor);
+ mPattern.Clear();
+ mPattern.AppendElement(0);
+ mIndex = 0;
+ mMonitor.Notify();
+}
+
+void
+EnsureVibratorThreadInitialized()
+{
+ if (sVibratorRunnable) {
+ return;
+ }
+
+ sVibratorRunnable = new VibratorRunnable();
+ nsCOMPtr<nsIThread> thread;
+ NS_NewThread(getter_AddRefs(thread), sVibratorRunnable);
+}
+
+} // namespace
+
+void
+Vibrate(const nsTArray<uint32_t> &pattern, const hal::WindowIdentifier &)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ if (VibratorRunnable::ShuttingDown()) {
+ return;
+ }
+ EnsureVibratorThreadInitialized();
+ sVibratorRunnable->Vibrate(pattern);
+}
+
+void
+CancelVibrate(const hal::WindowIdentifier &)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ if (VibratorRunnable::ShuttingDown()) {
+ return;
+ }
+ EnsureVibratorThreadInitialized();
+ sVibratorRunnable->CancelVibrate();
+}
+
+namespace {
+
+class BatteryUpdater : public Runnable {
+public:
+ NS_IMETHOD Run() override
+ {
+ hal::BatteryInformation info;
+ hal_impl::GetCurrentBatteryInformation(&info);
+
+ // Control the battery indicator (led light) here using BatteryInformation
+ // we just retrieved.
+ uint32_t color = 0; // Format: 0x00rrggbb.
+ if (info.charging() && (info.level() == 1)) {
+ // Charging and battery full.
+ color = BATTERY_FULL_ARGB;
+ } else if (info.charging() && (info.level() < 1)) {
+ // Charging but not full.
+ color = BATTERY_CHARGING_ARGB;
+ } // else turn off battery indicator.
+
+ LightConfiguration aConfig;
+ aConfig.light = eHalLightID_Battery;
+ aConfig.mode = eHalLightMode_User;
+ aConfig.flash = eHalLightFlash_None;
+ aConfig.flashOnMS = aConfig.flashOffMS = 0;
+ aConfig.color = color;
+
+ SetLight(eHalLightID_Battery, aConfig);
+
+ hal::NotifyBatteryChange(info);
+
+ {
+ // bug 975667
+ // Gecko gonk hal is required to emit battery charging/level notification via nsIObserverService.
+ // This is useful for XPCOM components that are not statically linked to Gecko and cannot call
+ // hal::EnableBatteryNotifications
+ nsCOMPtr<nsIObserverService> obsService = mozilla::services::GetObserverService();
+ nsCOMPtr<nsIWritablePropertyBag2> propbag =
+ do_CreateInstance("@mozilla.org/hash-property-bag;1");
+ if (obsService && propbag) {
+ propbag->SetPropertyAsBool(NS_LITERAL_STRING("charging"),
+ info.charging());
+ propbag->SetPropertyAsDouble(NS_LITERAL_STRING("level"),
+ info.level());
+
+ obsService->NotifyObservers(propbag, "gonkhal-battery-notifier", nullptr);
+ }
+ }
+
+ return NS_OK;
+ }
+};
+
+} // namespace
+
+class BatteryObserver final : public IUeventObserver
+{
+public:
+ NS_INLINE_DECL_REFCOUNTING(BatteryObserver)
+
+ BatteryObserver()
+ :mUpdater(new BatteryUpdater())
+ {
+ }
+
+ virtual void Notify(const NetlinkEvent &aEvent)
+ {
+ // this will run on IO thread
+ NetlinkEvent *event = const_cast<NetlinkEvent*>(&aEvent);
+ const char *subsystem = event->getSubsystem();
+ // e.g. DEVPATH=/devices/platform/sec-battery/power_supply/battery
+ const char *devpath = event->findParam("DEVPATH");
+ if (strcmp(subsystem, "power_supply") == 0 &&
+ strstr(devpath, "battery")) {
+ // aEvent will be valid only in this method.
+ NS_DispatchToMainThread(mUpdater);
+ }
+ }
+
+protected:
+ ~BatteryObserver() {}
+
+private:
+ RefPtr<BatteryUpdater> mUpdater;
+};
+
+// sBatteryObserver is owned by the IO thread. Only the IO thread may
+// create or destroy it.
+static StaticRefPtr<BatteryObserver> sBatteryObserver;
+
+static void
+RegisterBatteryObserverIOThread()
+{
+ MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
+ MOZ_ASSERT(!sBatteryObserver);
+
+ sBatteryObserver = new BatteryObserver();
+ RegisterUeventListener(sBatteryObserver);
+}
+
+void
+EnableBatteryNotifications()
+{
+ XRE_GetIOMessageLoop()->PostTask(
+ NewRunnableFunction(RegisterBatteryObserverIOThread));
+}
+
+static void
+UnregisterBatteryObserverIOThread()
+{
+ MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
+ MOZ_ASSERT(sBatteryObserver);
+
+ UnregisterUeventListener(sBatteryObserver);
+ sBatteryObserver = nullptr;
+}
+
+void
+DisableBatteryNotifications()
+{
+ XRE_GetIOMessageLoop()->PostTask(
+ NewRunnableFunction(UnregisterBatteryObserverIOThread));
+}
+
+static bool
+GetCurrentBatteryCharge(int* aCharge)
+{
+ bool success = ReadSysFile("/sys/class/power_supply/battery/capacity",
+ aCharge);
+ if (!success) {
+ return false;
+ }
+
+ #ifdef DEBUG
+ if ((*aCharge < 0) || (*aCharge > 100)) {
+ HAL_LOG("charge level contains unknown value: %d", *aCharge);
+ }
+ #endif
+
+ return (*aCharge >= 0) && (*aCharge <= 100);
+}
+
+static bool
+GetCurrentBatteryCharging(int* aCharging)
+{
+ static const DebugOnly<int> BATTERY_NOT_CHARGING = 0;
+ static const int BATTERY_CHARGING_USB = 1;
+ static const int BATTERY_CHARGING_AC = 2;
+
+ // Generic device support
+
+ int chargingSrc;
+ bool success =
+ ReadSysFile("/sys/class/power_supply/battery/charging_source", &chargingSrc);
+
+ if (success) {
+ #ifdef DEBUG
+ if (chargingSrc != BATTERY_NOT_CHARGING &&
+ chargingSrc != BATTERY_CHARGING_USB &&
+ chargingSrc != BATTERY_CHARGING_AC) {
+ HAL_LOG("charging_source contained unknown value: %d", chargingSrc);
+ }
+ #endif
+
+ *aCharging = (chargingSrc == BATTERY_CHARGING_USB ||
+ chargingSrc == BATTERY_CHARGING_AC);
+ return true;
+ }
+
+ // Otoro device support
+
+ char chargingSrcString[16];
+
+ success = ReadSysFile("/sys/class/power_supply/battery/status",
+ chargingSrcString, sizeof(chargingSrcString));
+ if (success) {
+ *aCharging = strcmp(chargingSrcString, "Charging") == 0 ||
+ strcmp(chargingSrcString, "Full") == 0;
+ return true;
+ }
+
+ return false;
+}
+
+void
+GetCurrentBatteryInformation(hal::BatteryInformation* aBatteryInfo)
+{
+ int charge;
+ static bool previousCharging = false;
+ static double previousLevel = 0.0, remainingTime = 0.0;
+ static struct timespec lastLevelChange;
+ struct timespec now;
+ double dtime, dlevel;
+
+ if (GetCurrentBatteryCharge(&charge)) {
+ aBatteryInfo->level() = (double)charge / 100.0;
+ } else {
+ aBatteryInfo->level() = dom::battery::kDefaultLevel;
+ }
+
+ int charging;
+
+ if (GetCurrentBatteryCharging(&charging)) {
+ aBatteryInfo->charging() = charging;
+ } else {
+ aBatteryInfo->charging() = true;
+ }
+
+ if (aBatteryInfo->charging() != previousCharging){
+ aBatteryInfo->remainingTime() = dom::battery::kUnknownRemainingTime;
+ memset(&lastLevelChange, 0, sizeof(struct timespec));
+ remainingTime = 0.0;
+ }
+
+ if (aBatteryInfo->charging()) {
+ if (aBatteryInfo->level() == 1.0) {
+ aBatteryInfo->remainingTime() = dom::battery::kDefaultRemainingTime;
+ } else if (aBatteryInfo->level() != previousLevel){
+ if (lastLevelChange.tv_sec != 0) {
+ clock_gettime(CLOCK_MONOTONIC, &now);
+ dtime = now.tv_sec - lastLevelChange.tv_sec;
+ dlevel = aBatteryInfo->level() - previousLevel;
+
+ if (dlevel <= 0.0) {
+ aBatteryInfo->remainingTime() = dom::battery::kUnknownRemainingTime;
+ } else {
+ remainingTime = (double) round(dtime / dlevel * (1.0 - aBatteryInfo->level()));
+ aBatteryInfo->remainingTime() = remainingTime;
+ }
+
+ lastLevelChange = now;
+ } else { // lastLevelChange.tv_sec == 0
+ clock_gettime(CLOCK_MONOTONIC, &lastLevelChange);
+ aBatteryInfo->remainingTime() = dom::battery::kUnknownRemainingTime;
+ }
+
+ } else {
+ clock_gettime(CLOCK_MONOTONIC, &now);
+ dtime = now.tv_sec - lastLevelChange.tv_sec;
+ if (dtime < remainingTime) {
+ aBatteryInfo->remainingTime() = round(remainingTime - dtime);
+ } else {
+ aBatteryInfo->remainingTime() = dom::battery::kUnknownRemainingTime;
+ }
+
+ }
+
+ } else {
+ if (aBatteryInfo->level() == 0.0) {
+ aBatteryInfo->remainingTime() = dom::battery::kDefaultRemainingTime;
+ } else if (aBatteryInfo->level() != previousLevel){
+ if (lastLevelChange.tv_sec != 0) {
+ clock_gettime(CLOCK_MONOTONIC, &now);
+ dtime = now.tv_sec - lastLevelChange.tv_sec;
+ dlevel = previousLevel - aBatteryInfo->level();
+
+ if (dlevel <= 0.0) {
+ aBatteryInfo->remainingTime() = dom::battery::kUnknownRemainingTime;
+ } else {
+ remainingTime = (double) round(dtime / dlevel * aBatteryInfo->level());
+ aBatteryInfo->remainingTime() = remainingTime;
+ }
+
+ lastLevelChange = now;
+ } else { // lastLevelChange.tv_sec == 0
+ clock_gettime(CLOCK_MONOTONIC, &lastLevelChange);
+ aBatteryInfo->remainingTime() = dom::battery::kUnknownRemainingTime;
+ }
+
+ } else {
+ clock_gettime(CLOCK_MONOTONIC, &now);
+ dtime = now.tv_sec - lastLevelChange.tv_sec;
+ if (dtime < remainingTime) {
+ aBatteryInfo->remainingTime() = round(remainingTime - dtime);
+ } else {
+ aBatteryInfo->remainingTime() = dom::battery::kUnknownRemainingTime;
+ }
+
+ }
+ }
+
+ previousCharging = aBatteryInfo->charging();
+ previousLevel = aBatteryInfo->level();
+}
+
+namespace {
+
+// We can write to screenEnabledFilename to enable/disable the screen, but when
+// we read, we always get "mem"! So we have to keep track ourselves whether
+// the screen is on or not.
+bool sScreenEnabled = true;
+
+// We can read wakeLockFilename to find out whether the cpu wake lock
+// is already acquired, but reading and parsing it is a lot more work
+// than tracking it ourselves, and it won't be accurate anyway (kernel
+// internal wake locks aren't counted here.)
+bool sCpuSleepAllowed = true;
+
+// Some CPU wake locks may be acquired internally in HAL. We use a counter to
+// keep track of these needs. Note we have to hold |sInternalLockCpuMutex|
+// when reading or writing this variable to ensure thread-safe.
+int32_t sInternalLockCpuCount = 0;
+
+} // namespace
+
+bool
+GetScreenEnabled()
+{
+ return sScreenEnabled;
+}
+
+void
+SetScreenEnabled(bool aEnabled)
+{
+ GetGonkDisplay()->SetEnabled(aEnabled);
+ sScreenEnabled = aEnabled;
+}
+
+bool
+GetKeyLightEnabled()
+{
+ LightConfiguration config;
+ bool ok = GetLight(eHalLightID_Buttons, &config);
+ if (ok) {
+ return (config.color != 0x00000000);
+ }
+ return false;
+}
+
+void
+SetKeyLightEnabled(bool aEnabled)
+{
+ LightConfiguration config;
+ config.mode = eHalLightMode_User;
+ config.flash = eHalLightFlash_None;
+ config.flashOnMS = config.flashOffMS = 0;
+ config.color = 0x00000000;
+
+ if (aEnabled) {
+ // Convert the value in [0, 1] to an int between 0 and 255 and then convert
+ // it to a color. Note that the high byte is FF, corresponding to the alpha
+ // channel.
+ double brightness = GetScreenBrightness();
+ uint32_t val = static_cast<int>(round(brightness * 255.0));
+ uint32_t color = (0xff<<24) + (val<<16) + (val<<8) + val;
+
+ config.color = color;
+ }
+
+ SetLight(eHalLightID_Buttons, config);
+ SetLight(eHalLightID_Keyboard, config);
+}
+
+double
+GetScreenBrightness()
+{
+ LightConfiguration config;
+ LightType light = eHalLightID_Backlight;
+
+ bool ok = GetLight(light, &config);
+ if (ok) {
+ // backlight is brightness only, so using one of the RGB elements as value.
+ int brightness = config.color & 0xFF;
+ return brightness / 255.0;
+ }
+ // If GetLight fails, it's because the light doesn't exist. So return
+ // a value corresponding to "off".
+ return 0;
+}
+
+void
+SetScreenBrightness(double brightness)
+{
+ // Don't use De Morgan's law to push the ! into this expression; we want to
+ // catch NaN too.
+ if (!(0 <= brightness && brightness <= 1)) {
+ HAL_LOG("SetScreenBrightness: Dropping illegal brightness %f.", brightness);
+ return;
+ }
+
+ // Convert the value in [0, 1] to an int between 0 and 255 and convert to a color
+ // note that the high byte is FF, corresponding to the alpha channel.
+ uint32_t val = static_cast<int>(round(brightness * 255.0));
+ uint32_t color = (0xff<<24) + (val<<16) + (val<<8) + val;
+
+ LightConfiguration config;
+ config.mode = eHalLightMode_User;
+ config.flash = eHalLightFlash_None;
+ config.flashOnMS = config.flashOffMS = 0;
+ config.color = color;
+ SetLight(eHalLightID_Backlight, config);
+ if (GetKeyLightEnabled()) {
+ SetLight(eHalLightID_Buttons, config);
+ SetLight(eHalLightID_Keyboard, config);
+ }
+}
+
+static StaticMutex sInternalLockCpuMutex;
+
+static void
+UpdateCpuSleepState()
+{
+ const char *wakeLockFilename = "/sys/power/wake_lock";
+ const char *wakeUnlockFilename = "/sys/power/wake_unlock";
+
+ sInternalLockCpuMutex.AssertCurrentThreadOwns();
+ bool allowed = sCpuSleepAllowed && !sInternalLockCpuCount;
+ WriteSysFile(allowed ? wakeUnlockFilename : wakeLockFilename, "gecko");
+}
+
+static void
+InternalLockCpu() {
+ StaticMutexAutoLock lock(sInternalLockCpuMutex);
+ ++sInternalLockCpuCount;
+ UpdateCpuSleepState();
+}
+
+static void
+InternalUnlockCpu() {
+ StaticMutexAutoLock lock(sInternalLockCpuMutex);
+ --sInternalLockCpuCount;
+ UpdateCpuSleepState();
+}
+
+bool
+GetCpuSleepAllowed()
+{
+ return sCpuSleepAllowed;
+}
+
+void
+SetCpuSleepAllowed(bool aAllowed)
+{
+ StaticMutexAutoLock lock(sInternalLockCpuMutex);
+ sCpuSleepAllowed = aAllowed;
+ UpdateCpuSleepState();
+}
+
+void
+AdjustSystemClock(int64_t aDeltaMilliseconds)
+{
+ int fd;
+ struct timespec now;
+
+ if (aDeltaMilliseconds == 0) {
+ return;
+ }
+
+ // Preventing context switch before setting system clock
+ sched_yield();
+ clock_gettime(CLOCK_REALTIME, &now);
+ now.tv_sec += (time_t)(aDeltaMilliseconds / 1000LL);
+ now.tv_nsec += (long)((aDeltaMilliseconds % 1000LL) * NsecPerMsec);
+ if (now.tv_nsec >= NsecPerSec) {
+ now.tv_sec += 1;
+ now.tv_nsec -= NsecPerSec;
+ }
+
+ if (now.tv_nsec < 0) {
+ now.tv_nsec += NsecPerSec;
+ now.tv_sec -= 1;
+ }
+
+ do {
+ fd = open("/dev/alarm", O_RDWR);
+ } while (fd == -1 && errno == EINTR);
+ ScopedClose autoClose(fd);
+ if (fd < 0) {
+ HAL_LOG("Failed to open /dev/alarm: %s", strerror(errno));
+ return;
+ }
+
+ if (ioctl(fd, ANDROID_ALARM_SET_RTC, &now) < 0) {
+ HAL_LOG("ANDROID_ALARM_SET_RTC failed: %s", strerror(errno));
+ }
+
+ hal::NotifySystemClockChange(aDeltaMilliseconds);
+}
+
+int32_t
+GetTimezoneOffset()
+{
+ PRExplodedTime prTime;
+ PR_ExplodeTime(PR_Now(), PR_LocalTimeParameters, &prTime);
+
+ // Daylight saving time (DST) will be taken into account.
+ int32_t offset = prTime.tm_params.tp_gmt_offset;
+ offset += prTime.tm_params.tp_dst_offset;
+
+ // Returns the timezone offset relative to UTC in minutes.
+ return -(offset / 60);
+}
+
+static int32_t sKernelTimezoneOffset = 0;
+
+static void
+UpdateKernelTimezone(int32_t timezoneOffset)
+{
+ if (sKernelTimezoneOffset == timezoneOffset) {
+ return;
+ }
+
+ // Tell the kernel about the new time zone as well, so that FAT filesystems
+ // will get local timestamps rather than UTC timestamps.
+ //
+ // We assume that /init.rc has a sysclktz entry so that settimeofday has
+ // already been called once before we call it (there is a side-effect in
+ // the kernel the very first time settimeofday is called where it does some
+ // special processing if you only set the timezone).
+ struct timezone tz;
+ memset(&tz, 0, sizeof(tz));
+ tz.tz_minuteswest = timezoneOffset;
+ settimeofday(nullptr, &tz);
+ sKernelTimezoneOffset = timezoneOffset;
+}
+
+void
+SetTimezone(const nsCString& aTimezoneSpec)
+{
+ if (aTimezoneSpec.Equals(GetTimezone())) {
+ // Even though the timezone hasn't changed, we still need to tell the
+ // kernel what the current timezone is. The timezone is persisted in
+ // a property and doesn't change across reboots, but the kernel still
+ // needs to be updated on every boot.
+ UpdateKernelTimezone(GetTimezoneOffset());
+ return;
+ }
+
+ int32_t oldTimezoneOffsetMinutes = GetTimezoneOffset();
+ property_set("persist.sys.timezone", aTimezoneSpec.get());
+ // This function is automatically called by the other time conversion
+ // functions that depend on the timezone. To be safe, we call it manually.
+ tzset();
+ int32_t newTimezoneOffsetMinutes = GetTimezoneOffset();
+ UpdateKernelTimezone(newTimezoneOffsetMinutes);
+ hal::NotifySystemTimezoneChange(
+ hal::SystemTimezoneChangeInformation(
+ oldTimezoneOffsetMinutes, newTimezoneOffsetMinutes));
+}
+
+nsCString
+GetTimezone()
+{
+ char timezone[32];
+ property_get("persist.sys.timezone", timezone, "");
+ return nsCString(timezone);
+}
+
+void
+EnableSystemClockChangeNotifications()
+{
+}
+
+void
+DisableSystemClockChangeNotifications()
+{
+}
+
+void
+EnableSystemTimezoneChangeNotifications()
+{
+}
+
+void
+DisableSystemTimezoneChangeNotifications()
+{
+}
+
+// Nothing to do here. Gonk widgetry always listens for screen
+// orientation changes.
+void
+EnableScreenConfigurationNotifications()
+{
+}
+
+void
+DisableScreenConfigurationNotifications()
+{
+}
+
+void
+GetCurrentScreenConfiguration(hal::ScreenConfiguration* aScreenConfiguration)
+{
+ RefPtr<nsScreenGonk> screen = nsScreenManagerGonk::GetPrimaryScreen();
+ *aScreenConfiguration = screen->GetConfiguration();
+}
+
+bool
+LockScreenOrientation(const dom::ScreenOrientationInternal& aOrientation)
+{
+ return OrientationObserver::GetInstance()->LockScreenOrientation(aOrientation);
+}
+
+void
+UnlockScreenOrientation()
+{
+ OrientationObserver::GetInstance()->UnlockScreenOrientation();
+}
+
+// This thread will wait for the alarm firing by a blocking IO.
+static pthread_t sAlarmFireWatcherThread;
+
+// If |sAlarmData| is non-null, it's owned by the alarm-watcher thread.
+struct AlarmData {
+public:
+ AlarmData(int aFd) : mFd(aFd),
+ mGeneration(sNextGeneration++),
+ mShuttingDown(false) {}
+ ScopedClose mFd;
+ int mGeneration;
+ bool mShuttingDown;
+
+ static int sNextGeneration;
+
+};
+
+int AlarmData::sNextGeneration = 0;
+
+AlarmData* sAlarmData = nullptr;
+
+class AlarmFiredEvent : public Runnable {
+public:
+ AlarmFiredEvent(int aGeneration) : mGeneration(aGeneration) {}
+
+ NS_IMETHOD Run() override {
+ // Guard against spurious notifications caused by an alarm firing
+ // concurrently with it being disabled.
+ if (sAlarmData && !sAlarmData->mShuttingDown &&
+ mGeneration == sAlarmData->mGeneration) {
+ hal::NotifyAlarmFired();
+ }
+ // The fired alarm event has been delivered to the observer (if needed);
+ // we can now release a CPU wake lock.
+ InternalUnlockCpu();
+ return NS_OK;
+ }
+
+private:
+ int mGeneration;
+};
+
+// Runs on alarm-watcher thread.
+static void
+DestroyAlarmData(void* aData)
+{
+ AlarmData* alarmData = static_cast<AlarmData*>(aData);
+ delete alarmData;
+}
+
+// Runs on alarm-watcher thread.
+void ShutDownAlarm(int aSigno)
+{
+ if (aSigno == SIGUSR1 && sAlarmData) {
+ sAlarmData->mShuttingDown = true;
+ }
+ return;
+}
+
+static void*
+WaitForAlarm(void* aData)
+{
+ pthread_cleanup_push(DestroyAlarmData, aData);
+
+ AlarmData* alarmData = static_cast<AlarmData*>(aData);
+
+ while (!alarmData->mShuttingDown) {
+ int alarmTypeFlags = 0;
+
+ // ALARM_WAIT apparently will block even if an alarm hasn't been
+ // programmed, although this behavior doesn't seem to be
+ // documented. We rely on that here to avoid spinning the CPU
+ // while awaiting an alarm to be programmed.
+ do {
+ alarmTypeFlags = ioctl(alarmData->mFd, ANDROID_ALARM_WAIT);
+ } while (alarmTypeFlags < 0 && errno == EINTR &&
+ !alarmData->mShuttingDown);
+
+ if (!alarmData->mShuttingDown && alarmTypeFlags >= 0 &&
+ (alarmTypeFlags & ANDROID_ALARM_RTC_WAKEUP_MASK)) {
+ // To make sure the observer can get the alarm firing notification
+ // *on time* (the system won't sleep during the process in any way),
+ // we need to acquire a CPU wake lock before firing the alarm event.
+ InternalLockCpu();
+ RefPtr<AlarmFiredEvent> event =
+ new AlarmFiredEvent(alarmData->mGeneration);
+ NS_DispatchToMainThread(event);
+ }
+ }
+
+ pthread_cleanup_pop(1);
+ return nullptr;
+}
+
+bool
+EnableAlarm()
+{
+ MOZ_ASSERT(!sAlarmData);
+
+ int alarmFd = open("/dev/alarm", O_RDWR);
+ if (alarmFd < 0) {
+ HAL_LOG("Failed to open alarm device: %s.", strerror(errno));
+ return false;
+ }
+
+ UniquePtr<AlarmData> alarmData = MakeUnique<AlarmData>(alarmFd);
+
+ struct sigaction actions;
+ memset(&actions, 0, sizeof(actions));
+ sigemptyset(&actions.sa_mask);
+ actions.sa_flags = 0;
+ actions.sa_handler = ShutDownAlarm;
+ if (sigaction(SIGUSR1, &actions, nullptr)) {
+ HAL_LOG("Failed to set SIGUSR1 signal for alarm-watcher thread.");
+ return false;
+ }
+
+ pthread_attr_t attr;
+ pthread_attr_init(&attr);
+ pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+
+ int status = pthread_create(&sAlarmFireWatcherThread, &attr, WaitForAlarm,
+ alarmData.get());
+ if (status) {
+ alarmData.reset();
+ HAL_LOG("Failed to create alarm-watcher thread. Status: %d.", status);
+ return false;
+ }
+
+ pthread_attr_destroy(&attr);
+
+ // The thread owns this now. We only hold a pointer.
+ sAlarmData = alarmData.release();
+ return true;
+}
+
+void
+DisableAlarm()
+{
+ MOZ_ASSERT(sAlarmData);
+
+ // NB: this must happen-before the thread cancellation.
+ sAlarmData = nullptr;
+
+ // The cancel will interrupt the thread and destroy it, freeing the
+ // data pointed at by sAlarmData.
+ DebugOnly<int> err = pthread_kill(sAlarmFireWatcherThread, SIGUSR1);
+ MOZ_ASSERT(!err);
+}
+
+bool
+SetAlarm(int32_t aSeconds, int32_t aNanoseconds)
+{
+ if (!sAlarmData) {
+ HAL_LOG("We should have enabled the alarm.");
+ return false;
+ }
+
+ struct timespec ts;
+ ts.tv_sec = aSeconds;
+ ts.tv_nsec = aNanoseconds;
+
+ // Currently we only support RTC wakeup alarm type.
+ const int result = ioctl(sAlarmData->mFd,
+ ANDROID_ALARM_SET(ANDROID_ALARM_RTC_WAKEUP), &ts);
+
+ if (result < 0) {
+ HAL_LOG("Unable to set alarm: %s.", strerror(errno));
+ return false;
+ }
+
+ return true;
+}
+
+static int
+OomAdjOfOomScoreAdj(int aOomScoreAdj)
+{
+ // Convert OOM adjustment from the domain of /proc/<pid>/oom_score_adj
+ // to the domain of /proc/<pid>/oom_adj.
+
+ int adj;
+
+ if (aOomScoreAdj < 0) {
+ adj = (OOM_DISABLE * aOomScoreAdj) / OOM_SCORE_ADJ_MIN;
+ } else {
+ adj = (OOM_ADJUST_MAX * aOomScoreAdj) / OOM_SCORE_ADJ_MAX;
+ }
+
+ return adj;
+}
+
+static void
+RoundOomScoreAdjUpWithLRU(int& aOomScoreAdj, uint32_t aLRU)
+{
+ // We want to add minimum value to round OomScoreAdj up according to
+ // the steps by aLRU.
+ aOomScoreAdj +=
+ ceil(((float)OOM_SCORE_ADJ_MAX / OOM_ADJUST_MAX) * aLRU);
+}
+
+#define OOM_LOG(level, args...) __android_log_print(level, "OomLogger", ##args)
+class OomVictimLogger final
+ : public nsIObserver
+{
+public:
+ OomVictimLogger()
+ : mLastLineChecked(-1.0),
+ mRegexes(nullptr)
+ {
+ // Enable timestamps in kernel's printk
+ WriteSysFile("/sys/module/printk/parameters/time", "Y");
+ }
+
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIOBSERVER
+
+protected:
+ ~OomVictimLogger() {}
+
+private:
+ double mLastLineChecked;
+ UniqueFreePtr<regex_t> mRegexes;
+};
+NS_IMPL_ISUPPORTS(OomVictimLogger, nsIObserver);
+
+NS_IMETHODIMP
+OomVictimLogger::Observe(
+ nsISupports* aSubject,
+ const char* aTopic,
+ const char16_t* aData)
+{
+ nsDependentCString event_type(aTopic);
+ if (!event_type.EqualsLiteral("ipc:content-shutdown")) {
+ return NS_OK;
+ }
+
+ // OOM message finding regexes
+ const char* const regexes_raw[] = {
+ ".*select.*to kill.*",
+ ".*send sigkill to.*",
+ ".*lowmem_shrink.*",
+ ".*[Oo]ut of [Mm]emory.*",
+ ".*[Kk]ill [Pp]rocess.*",
+ ".*[Kk]illed [Pp]rocess.*",
+ ".*oom-killer.*",
+ // The regexes below are for the output of dump_task from oom_kill.c
+ // 1st - title 2nd - body lines (8 ints and a string)
+ // oom_adj and oom_score_adj can be negative
+ "\\[ pid \\] uid tgid total_vm rss cpu oom_adj oom_score_adj name",
+ "\\[.*[0-9][0-9]*\\][ ]*[0-9][0-9]*[ ]*[0-9][0-9]*[ ]*[0-9][0-9]*[ ]*[0-9][0-9]*[ ]*[0-9][0-9]*[ ]*.[0-9][0-9]*[ ]*.[0-9][0-9]*.*"
+ };
+ const size_t regex_count = ArrayLength(regexes_raw);
+
+ // Compile our regex just in time
+ if (!mRegexes) {
+ UniqueFreePtr<regex_t> regexes(
+ static_cast<regex_t*>(malloc(sizeof(regex_t) * regex_count))
+ );
+ mRegexes.swap(regexes);
+ for (size_t i = 0; i < regex_count; i++) {
+ int compilation_err =
+ regcomp(&(mRegexes.get()[i]), regexes_raw[i], REG_NOSUB);
+ if (compilation_err) {
+ OOM_LOG(ANDROID_LOG_ERROR, "Cannot compile regex \"%s\"\n", regexes_raw[i]);
+ return NS_OK;
+ }
+ }
+ }
+
+#ifndef KLOG_SIZE_BUFFER
+ // Upstream bionic in commit
+ // e249b059637b49a285ed9f58a2a18bfd054e5d95
+ // deprecated the old klog defs.
+ // Our current bionic does not hit this
+ // change yet so handle the future change.
+ // (ICS doesn't have KLOG_SIZE_BUFFER but
+ // JB and onwards does.)
+ #define KLOG_SIZE_BUFFER KLOG_WRITE
+#endif
+ // Retreive kernel log
+ int msg_buf_size = klogctl(KLOG_SIZE_BUFFER, NULL, 0);
+ UniqueFreePtr<char> msg_buf(static_cast<char *>(malloc(msg_buf_size + 1)));
+ int read_size = klogctl(KLOG_READ_ALL, msg_buf.get(), msg_buf_size);
+
+ // Turn buffer into cstring
+ read_size = read_size > msg_buf_size ? msg_buf_size : read_size;
+ msg_buf.get()[read_size] = '\0';
+
+ // Foreach line
+ char* line_end;
+ char* line_begin = msg_buf.get();
+ for (; (line_end = strchr(line_begin, '\n')); line_begin = line_end + 1) {
+ // make line into cstring
+ *line_end = '\0';
+
+ // Note: Kernel messages look like:
+ // <5>[63648.286409] sd 35:0:0:0: Attached scsi generic sg1 type 0
+ // 5 is the loging level
+ // [*] is the time timestamp, seconds since boot
+ // last comes the logged message
+
+ // Since the logging level can be a string we must
+ // skip it since scanf lacks wildcard matching
+ char* timestamp_begin = strchr(line_begin, '[');
+ char after_float;
+ double lineTimestamp = -1;
+ bool lineTimestampFound = false;
+ if (timestamp_begin &&
+ // Note: scanf treats a ' ' as [ ]*
+ // Note: scanf treats [ %lf] as [ %lf thus we must check
+ // for the closing bracket outselves.
+ 2 == sscanf(timestamp_begin, "[ %lf%c", &lineTimestamp, &after_float) &&
+ after_float == ']') {
+ if (lineTimestamp <= mLastLineChecked) {
+ continue;
+ }
+
+ lineTimestampFound = true;
+ mLastLineChecked = lineTimestamp;
+ }
+
+ // Log interesting lines
+ for (size_t i = 0; i < regex_count; i++) {
+ int matching = !regexec(&(mRegexes.get()[i]), line_begin, 0, NULL, 0);
+ if (matching) {
+ // Log content of kernel message. We try to skip the ], but if for
+ // some reason (most likely due to buffer overflow/wraparound), we
+ // can't find the ] then we just log the entire line.
+ char* endOfTimestamp = strchr(line_begin, ']');
+ if (endOfTimestamp && endOfTimestamp[1] == ' ') {
+ // skip the ] and the space that follows it
+ line_begin = endOfTimestamp + 2;
+ }
+ if (!lineTimestampFound) {
+ OOM_LOG(ANDROID_LOG_WARN, "following kill message may be a duplicate");
+ }
+ OOM_LOG(ANDROID_LOG_ERROR, "[Kill]: %s\n", line_begin);
+ break;
+ }
+ }
+ }
+
+ return NS_OK;
+}
+
+/**
+ * Wraps a particular ProcessPriority, giving us easy access to the prefs that
+ * are relevant to it.
+ *
+ * Creating a PriorityClass also ensures that the control group is created.
+ */
+class PriorityClass
+{
+public:
+ /**
+ * Create a PriorityClass for the given ProcessPriority. This implicitly
+ * reads the relevant prefs and opens the cgroup.procs file of the relevant
+ * control group caching its file descriptor for later use.
+ */
+ PriorityClass(ProcessPriority aPriority);
+
+ /**
+ * Closes the file descriptor for the cgroup.procs file of the associated
+ * control group.
+ */
+ ~PriorityClass();
+
+ PriorityClass(const PriorityClass& aOther);
+ PriorityClass& operator=(const PriorityClass& aOther);
+
+ ProcessPriority Priority()
+ {
+ return mPriority;
+ }
+
+ int32_t OomScoreAdj()
+ {
+ return clamped<int32_t>(mOomScoreAdj, OOM_SCORE_ADJ_MIN, OOM_SCORE_ADJ_MAX);
+ }
+
+ int32_t KillUnderKB()
+ {
+ return mKillUnderKB;
+ }
+
+ nsCString CGroup()
+ {
+ return mGroup;
+ }
+
+ /**
+ * Adds a process to this priority class, this moves the process' PID into
+ * the associated control group.
+ *
+ * @param aPid The PID of the process to be added.
+ */
+ void AddProcess(int aPid);
+
+private:
+ ProcessPriority mPriority;
+ int32_t mOomScoreAdj;
+ int32_t mKillUnderKB;
+ int mCpuCGroupProcsFd;
+ int mMemCGroupProcsFd;
+ nsCString mGroup;
+
+ /**
+ * Return a string that identifies where we can find the value of aPref
+ * that's specific to mPriority. For example, we might return
+ * "hal.processPriorityManager.gonk.FOREGROUND_HIGH.oomScoreAdjust".
+ */
+ nsCString PriorityPrefName(const char* aPref)
+ {
+ return nsPrintfCString("hal.processPriorityManager.gonk.%s.%s",
+ ProcessPriorityToString(mPriority), aPref);
+ }
+
+ /**
+ * Get the full path of the cgroup.procs file associated with the group.
+ */
+ nsCString CpuCGroupProcsFilename()
+ {
+ nsCString cgroupName = mGroup;
+
+ /* If mGroup is empty, our cgroup.procs file is the root procs file,
+ * located at /dev/cpuctl/cgroup.procs. Otherwise our procs file is
+ * /dev/cpuctl/NAME/cgroup.procs. */
+
+ if (!mGroup.IsEmpty()) {
+ cgroupName.AppendLiteral("/");
+ }
+
+ return NS_LITERAL_CSTRING("/dev/cpuctl/") + cgroupName +
+ NS_LITERAL_CSTRING("cgroup.procs");
+ }
+
+ nsCString MemCGroupProcsFilename()
+ {
+ nsCString cgroupName = mGroup;
+
+ /* If mGroup is empty, our cgroup.procs file is the root procs file,
+ * located at /sys/fs/cgroup/memory/cgroup.procs. Otherwise our procs
+ * file is /sys/fs/cgroup/memory/NAME/cgroup.procs. */
+
+ if (!mGroup.IsEmpty()) {
+ cgroupName.AppendLiteral("/");
+ }
+
+ return NS_LITERAL_CSTRING("/sys/fs/cgroup/memory/") + cgroupName +
+ NS_LITERAL_CSTRING("cgroup.procs");
+ }
+
+ int OpenCpuCGroupProcs()
+ {
+ return open(CpuCGroupProcsFilename().get(), O_WRONLY);
+ }
+
+ int OpenMemCGroupProcs()
+ {
+ return open(MemCGroupProcsFilename().get(), O_WRONLY);
+ }
+};
+
+/**
+ * Try to create the cgroup for the given PriorityClass, if it doesn't already
+ * exist. This essentially implements mkdir -p; that is, we create parent
+ * cgroups as necessary. The group parameters are also set according to
+ * the corresponding preferences.
+ *
+ * @param aGroup The name of the group.
+ * @return true if we successfully created the cgroup, or if it already
+ * exists. Otherwise, return false.
+ */
+static bool
+EnsureCpuCGroupExists(const nsACString &aGroup)
+{
+ NS_NAMED_LITERAL_CSTRING(kDevCpuCtl, "/dev/cpuctl/");
+ NS_NAMED_LITERAL_CSTRING(kSlash, "/");
+
+ nsAutoCString groupName(aGroup);
+ HAL_LOG("EnsureCpuCGroupExists for group '%s'", groupName.get());
+
+ nsAutoCString prefPrefix("hal.processPriorityManager.gonk.cgroups.");
+
+ /* If cgroup is not empty, append the cgroup name and a dot to obtain the
+ * group specific preferences. */
+ if (!aGroup.IsEmpty()) {
+ prefPrefix += aGroup + NS_LITERAL_CSTRING(".");
+ }
+
+ nsAutoCString cpuSharesPref(prefPrefix + NS_LITERAL_CSTRING("cpu_shares"));
+ int cpuShares = Preferences::GetInt(cpuSharesPref.get());
+
+ nsAutoCString cpuNotifyOnMigratePref(prefPrefix
+ + NS_LITERAL_CSTRING("cpu_notify_on_migrate"));
+ int cpuNotifyOnMigrate = Preferences::GetInt(cpuNotifyOnMigratePref.get());
+
+ // Create mCGroup and its parent directories, as necessary.
+ nsCString cgroupIter = aGroup + kSlash;
+
+ int32_t offset = 0;
+ while ((offset = cgroupIter.FindChar('/', offset)) != -1) {
+ nsAutoCString path = kDevCpuCtl + Substring(cgroupIter, 0, offset);
+ int rv = mkdir(path.get(), 0744);
+
+ if (rv == -1 && errno != EEXIST) {
+ HAL_LOG("Could not create the %s control group.", path.get());
+ return false;
+ }
+
+ offset++;
+ }
+ HAL_LOG("EnsureCpuCGroupExists created group '%s'", groupName.get());
+
+ nsAutoCString pathPrefix(kDevCpuCtl + aGroup + kSlash);
+ nsAutoCString cpuSharesPath(pathPrefix + NS_LITERAL_CSTRING("cpu.shares"));
+ if (cpuShares && !WriteSysFile(cpuSharesPath.get(),
+ nsPrintfCString("%d", cpuShares).get())) {
+ HAL_LOG("Could not set the cpu share for group %s", cpuSharesPath.get());
+ return false;
+ }
+
+ nsAutoCString notifyOnMigratePath(pathPrefix
+ + NS_LITERAL_CSTRING("cpu.notify_on_migrate"));
+ if (!WriteSysFile(notifyOnMigratePath.get(),
+ nsPrintfCString("%d", cpuNotifyOnMigrate).get())) {
+ HAL_LOG("Could not set the cpu migration notification flag for group %s",
+ notifyOnMigratePath.get());
+ return false;
+ }
+
+ return true;
+}
+
+static bool
+EnsureMemCGroupExists(const nsACString &aGroup)
+{
+ NS_NAMED_LITERAL_CSTRING(kMemCtl, "/sys/fs/cgroup/memory/");
+ NS_NAMED_LITERAL_CSTRING(kSlash, "/");
+
+ nsAutoCString groupName(aGroup);
+ HAL_LOG("EnsureMemCGroupExists for group '%s'", groupName.get());
+
+ nsAutoCString prefPrefix("hal.processPriorityManager.gonk.cgroups.");
+
+ /* If cgroup is not empty, append the cgroup name and a dot to obtain the
+ * group specific preferences. */
+ if (!aGroup.IsEmpty()) {
+ prefPrefix += aGroup + NS_LITERAL_CSTRING(".");
+ }
+
+ nsAutoCString memSwappinessPref(prefPrefix + NS_LITERAL_CSTRING("memory_swappiness"));
+ int memSwappiness = Preferences::GetInt(memSwappinessPref.get());
+
+ // Create mCGroup and its parent directories, as necessary.
+ nsCString cgroupIter = aGroup + kSlash;
+
+ int32_t offset = 0;
+ while ((offset = cgroupIter.FindChar('/', offset)) != -1) {
+ nsAutoCString path = kMemCtl + Substring(cgroupIter, 0, offset);
+ int rv = mkdir(path.get(), 0744);
+
+ if (rv == -1 && errno != EEXIST) {
+ HAL_LOG("Could not create the %s control group.", path.get());
+ return false;
+ }
+
+ offset++;
+ }
+ HAL_LOG("EnsureMemCGroupExists created group '%s'", groupName.get());
+
+ nsAutoCString pathPrefix(kMemCtl + aGroup + kSlash);
+ nsAutoCString memSwappinessPath(pathPrefix + NS_LITERAL_CSTRING("memory.swappiness"));
+ if (!WriteSysFile(memSwappinessPath.get(),
+ nsPrintfCString("%d", memSwappiness).get())) {
+ HAL_LOG("Could not set the memory.swappiness for group %s", memSwappinessPath.get());
+ return false;
+ }
+ HAL_LOG("Set memory.swappiness for group %s to %d", memSwappinessPath.get(), memSwappiness);
+
+ return true;
+}
+
+PriorityClass::PriorityClass(ProcessPriority aPriority)
+ : mPriority(aPriority)
+ , mOomScoreAdj(0)
+ , mKillUnderKB(0)
+ , mCpuCGroupProcsFd(-1)
+ , mMemCGroupProcsFd(-1)
+{
+ DebugOnly<nsresult> rv;
+
+ rv = Preferences::GetInt(PriorityPrefName("OomScoreAdjust").get(),
+ &mOomScoreAdj);
+ MOZ_ASSERT(NS_SUCCEEDED(rv), "Missing oom_score_adj preference");
+
+ rv = Preferences::GetInt(PriorityPrefName("KillUnderKB").get(),
+ &mKillUnderKB);
+
+ rv = Preferences::GetCString(PriorityPrefName("cgroup").get(), &mGroup);
+ MOZ_ASSERT(NS_SUCCEEDED(rv), "Missing control group preference");
+
+ if (EnsureCpuCGroupExists(mGroup)) {
+ mCpuCGroupProcsFd = OpenCpuCGroupProcs();
+ }
+ if (EnsureMemCGroupExists(mGroup)) {
+ mMemCGroupProcsFd = OpenMemCGroupProcs();
+ }
+}
+
+PriorityClass::~PriorityClass()
+{
+ if (mCpuCGroupProcsFd != -1) {
+ close(mCpuCGroupProcsFd);
+ }
+ if (mMemCGroupProcsFd != -1) {
+ close(mMemCGroupProcsFd);
+ }
+}
+
+PriorityClass::PriorityClass(const PriorityClass& aOther)
+ : mPriority(aOther.mPriority)
+ , mOomScoreAdj(aOther.mOomScoreAdj)
+ , mKillUnderKB(aOther.mKillUnderKB)
+ , mGroup(aOther.mGroup)
+{
+ mCpuCGroupProcsFd = OpenCpuCGroupProcs();
+ mMemCGroupProcsFd = OpenMemCGroupProcs();
+}
+
+PriorityClass& PriorityClass::operator=(const PriorityClass& aOther)
+{
+ mPriority = aOther.mPriority;
+ mOomScoreAdj = aOther.mOomScoreAdj;
+ mKillUnderKB = aOther.mKillUnderKB;
+ mGroup = aOther.mGroup;
+ mCpuCGroupProcsFd = OpenCpuCGroupProcs();
+ mMemCGroupProcsFd = OpenMemCGroupProcs();
+ return *this;
+}
+
+void PriorityClass::AddProcess(int aPid)
+{
+ if (mCpuCGroupProcsFd >= 0) {
+ nsPrintfCString str("%d", aPid);
+
+ if (write(mCpuCGroupProcsFd, str.get(), strlen(str.get())) < 0) {
+ HAL_ERR("Couldn't add PID %d to the %s cpu control group", aPid, mGroup.get());
+ }
+ }
+ if (mMemCGroupProcsFd >= 0) {
+ nsPrintfCString str("%d", aPid);
+
+ if (write(mMemCGroupProcsFd, str.get(), strlen(str.get())) < 0) {
+ HAL_ERR("Couldn't add PID %d to the %s memory control group", aPid, mGroup.get());
+ }
+ }
+}
+
+/**
+ * Get the PriorityClass associated with the given ProcessPriority.
+ *
+ * If you pass an invalid ProcessPriority value, we return null.
+ *
+ * The pointers returned here are owned by GetPriorityClass (don't free them
+ * yourself). They are guaranteed to stick around until shutdown.
+ */
+PriorityClass*
+GetPriorityClass(ProcessPriority aPriority)
+{
+ static StaticAutoPtr<nsTArray<PriorityClass>> priorityClasses;
+
+ // Initialize priorityClasses if this is the first time we're running this
+ // method.
+ if (!priorityClasses) {
+ priorityClasses = new nsTArray<PriorityClass>();
+ ClearOnShutdown(&priorityClasses);
+
+ for (int32_t i = 0; i < NUM_PROCESS_PRIORITY; i++) {
+ priorityClasses->AppendElement(PriorityClass(ProcessPriority(i)));
+ }
+ }
+
+ if (aPriority < 0 ||
+ static_cast<uint32_t>(aPriority) >= priorityClasses->Length()) {
+ return nullptr;
+ }
+
+ return &(*priorityClasses)[aPriority];
+}
+
+static void
+EnsureKernelLowMemKillerParamsSet()
+{
+ static bool kernelLowMemKillerParamsSet;
+ if (kernelLowMemKillerParamsSet) {
+ return;
+ }
+ kernelLowMemKillerParamsSet = true;
+
+ HAL_LOG("Setting kernel's low-mem killer parameters.");
+
+ // Set /sys/module/lowmemorykiller/parameters/{adj,minfree,notify_trigger}
+ // according to our prefs. These files let us tune when the kernel kills
+ // processes when we're low on memory, and when it notifies us that we're
+ // running low on available memory.
+ //
+ // adj and minfree are both comma-separated lists of integers. If adj="A,B"
+ // and minfree="X,Y", then the kernel will kill processes with oom_adj
+ // A or higher once we have fewer than X pages of memory free, and will kill
+ // processes with oom_adj B or higher once we have fewer than Y pages of
+ // memory free.
+ //
+ // notify_trigger is a single integer. If we set notify_trigger=Z, then
+ // we'll get notified when there are fewer than Z pages of memory free. (See
+ // GonkMemoryPressureMonitoring.cpp.)
+
+ // Build the adj and minfree strings.
+ nsAutoCString adjParams;
+ nsAutoCString minfreeParams;
+
+ DebugOnly<int32_t> lowerBoundOfNextOomScoreAdj = OOM_SCORE_ADJ_MIN - 1;
+ DebugOnly<int32_t> lowerBoundOfNextKillUnderKB = 0;
+ int32_t countOfLowmemorykillerParametersSets = 0;
+
+ long page_size = sysconf(_SC_PAGESIZE);
+
+ for (int i = NUM_PROCESS_PRIORITY - 1; i >= 0; i--) {
+ // The system doesn't function correctly if we're missing these prefs, so
+ // crash loudly.
+
+ PriorityClass* pc = GetPriorityClass(static_cast<ProcessPriority>(i));
+
+ int32_t oomScoreAdj = pc->OomScoreAdj();
+ int32_t killUnderKB = pc->KillUnderKB();
+
+ if (killUnderKB == 0) {
+ // ProcessPriority values like PROCESS_PRIORITY_FOREGROUND_KEYBOARD,
+ // which has only OomScoreAdjust but lacks KillUnderMB value, will not
+ // create new LMK parameters.
+ continue;
+ }
+
+ // The LMK in kernel silently malfunctions if we assign the parameters
+ // in non-increasing order, so we add this assertion here. See bug 887192.
+ MOZ_ASSERT(oomScoreAdj > lowerBoundOfNextOomScoreAdj);
+ MOZ_ASSERT(killUnderKB > lowerBoundOfNextKillUnderKB);
+
+ // The LMK in kernel only accept 6 sets of LMK parameters. See bug 914728.
+ MOZ_ASSERT(countOfLowmemorykillerParametersSets < 6);
+
+ // adj is in oom_adj units.
+ adjParams.AppendPrintf("%d,", OomAdjOfOomScoreAdj(oomScoreAdj));
+
+ // minfree is in pages.
+ minfreeParams.AppendPrintf("%ld,", killUnderKB * 1024 / page_size);
+
+ lowerBoundOfNextOomScoreAdj = oomScoreAdj;
+ lowerBoundOfNextKillUnderKB = killUnderKB;
+ countOfLowmemorykillerParametersSets++;
+ }
+
+ // Strip off trailing commas.
+ adjParams.Cut(adjParams.Length() - 1, 1);
+ minfreeParams.Cut(minfreeParams.Length() - 1, 1);
+ if (!adjParams.IsEmpty() && !minfreeParams.IsEmpty()) {
+ WriteSysFile("/sys/module/lowmemorykiller/parameters/adj", adjParams.get());
+ WriteSysFile("/sys/module/lowmemorykiller/parameters/minfree",
+ minfreeParams.get());
+ }
+
+ // Set the low-memory-notification threshold.
+ int32_t lowMemNotifyThresholdKB;
+ if (NS_SUCCEEDED(Preferences::GetInt(
+ "hal.processPriorityManager.gonk.notifyLowMemUnderKB",
+ &lowMemNotifyThresholdKB))) {
+
+ // notify_trigger is in pages.
+ WriteSysFile("/sys/module/lowmemorykiller/parameters/notify_trigger",
+ nsPrintfCString("%ld", lowMemNotifyThresholdKB * 1024 / page_size).get());
+ }
+
+ // Ensure OOM events appear in logcat
+ RefPtr<OomVictimLogger> oomLogger = new OomVictimLogger();
+ nsCOMPtr<nsIObserverService> os = services::GetObserverService();
+ if (os) {
+ os->AddObserver(oomLogger, "ipc:content-shutdown", false);
+ }
+}
+
+void
+SetProcessPriority(int aPid, ProcessPriority aPriority, uint32_t aLRU)
+{
+ HAL_LOG("SetProcessPriority(pid=%d, priority=%d, LRU=%u)",
+ aPid, aPriority, aLRU);
+
+ // If this is the first time SetProcessPriority was called, set the kernel's
+ // OOM parameters according to our prefs.
+ //
+ // We could/should do this on startup instead of waiting for the first
+ // SetProcessPriorityCall. But in practice, the master process needs to set
+ // its priority early in the game, so we can reasonably rely on
+ // SetProcessPriority being called early in startup.
+ EnsureKernelLowMemKillerParamsSet();
+
+ PriorityClass* pc = GetPriorityClass(aPriority);
+
+ int oomScoreAdj = pc->OomScoreAdj();
+
+ RoundOomScoreAdjUpWithLRU(oomScoreAdj, aLRU);
+
+ // We try the newer interface first, and fall back to the older interface
+ // on failure.
+ if (!WriteSysFile(nsPrintfCString("/proc/%d/oom_score_adj", aPid).get(),
+ nsPrintfCString("%d", oomScoreAdj).get()))
+ {
+ WriteSysFile(nsPrintfCString("/proc/%d/oom_adj", aPid).get(),
+ nsPrintfCString("%d", OomAdjOfOomScoreAdj(oomScoreAdj)).get());
+ }
+
+ HAL_LOG("Assigning pid %d to cgroup %s", aPid, pc->CGroup().get());
+ pc->AddProcess(aPid);
+}
+
+static bool
+IsValidRealTimePriority(int aValue, int aSchedulePolicy)
+{
+ return (aValue >= sched_get_priority_min(aSchedulePolicy)) &&
+ (aValue <= sched_get_priority_max(aSchedulePolicy));
+}
+
+static void
+SetThreadNiceValue(pid_t aTid, ThreadPriority aThreadPriority, int aValue)
+{
+ MOZ_ASSERT(aThreadPriority < NUM_THREAD_PRIORITY);
+ MOZ_ASSERT(aThreadPriority >= 0);
+
+ HAL_LOG("Setting thread %d to priority level %s; nice level %d",
+ aTid, ThreadPriorityToString(aThreadPriority), aValue);
+ int rv = setpriority(PRIO_PROCESS, aTid, aValue);
+
+ if (rv) {
+ HAL_LOG("Failed to set thread %d to priority level %s; error %s", aTid,
+ ThreadPriorityToString(aThreadPriority), strerror(errno));
+ }
+}
+
+static void
+SetRealTimeThreadPriority(pid_t aTid,
+ ThreadPriority aThreadPriority,
+ int aValue)
+{
+ int policy = SCHED_FIFO;
+
+ MOZ_ASSERT(aThreadPriority < NUM_THREAD_PRIORITY);
+ MOZ_ASSERT(aThreadPriority >= 0);
+ MOZ_ASSERT(IsValidRealTimePriority(aValue, policy), "Invalid real time priority");
+
+ // Setting real time priorities requires using sched_setscheduler
+ HAL_LOG("Setting thread %d to priority level %s; Real Time priority %d, "
+ "Schedule FIFO", aTid, ThreadPriorityToString(aThreadPriority),
+ aValue);
+ sched_param schedParam;
+ schedParam.sched_priority = aValue;
+ int rv = sched_setscheduler(aTid, policy, &schedParam);
+
+ if (rv) {
+ HAL_LOG("Failed to set thread %d to real time priority level %s; error %s",
+ aTid, ThreadPriorityToString(aThreadPriority), strerror(errno));
+ }
+}
+
+/*
+ * Used to store the nice value adjustments and real time priorities for the
+ * various thread priority levels.
+ */
+struct ThreadPriorityPrefs {
+ bool initialized;
+ struct {
+ int nice;
+ int realTime;
+ } priorities[NUM_THREAD_PRIORITY];
+};
+
+/*
+ * Reads the preferences for the various process priority levels and sets up
+ * watchers so that if they're dynamically changed the change is reflected on
+ * the appropriate variables.
+ */
+void
+EnsureThreadPriorityPrefs(ThreadPriorityPrefs* prefs)
+{
+ if (prefs->initialized) {
+ return;
+ }
+
+ for (int i = THREAD_PRIORITY_COMPOSITOR; i < NUM_THREAD_PRIORITY; i++) {
+ ThreadPriority priority = static_cast<ThreadPriority>(i);
+
+ // Read the nice values
+ const char* threadPriorityStr = ThreadPriorityToString(priority);
+ nsPrintfCString niceStr("hal.gonk.%s.nice", threadPriorityStr);
+ Preferences::AddIntVarCache(&prefs->priorities[i].nice, niceStr.get());
+
+ // Read the real-time priorities
+ nsPrintfCString realTimeStr("hal.gonk.%s.rt_priority", threadPriorityStr);
+ Preferences::AddIntVarCache(&prefs->priorities[i].realTime,
+ realTimeStr.get());
+ }
+
+ prefs->initialized = true;
+}
+
+static void
+DoSetThreadPriority(pid_t aTid, hal::ThreadPriority aThreadPriority)
+{
+ // See bug 999115, we can only read preferences on the main thread otherwise
+ // we create a race condition in HAL
+ MOZ_ASSERT(NS_IsMainThread(), "Can only set thread priorities on main thread");
+ MOZ_ASSERT(aThreadPriority >= 0);
+
+ static ThreadPriorityPrefs prefs = { 0 };
+ EnsureThreadPriorityPrefs(&prefs);
+
+ switch (aThreadPriority) {
+ case THREAD_PRIORITY_COMPOSITOR:
+ break;
+ default:
+ HAL_ERR("Unrecognized thread priority %d; Doing nothing",
+ aThreadPriority);
+ return;
+ }
+
+ int realTimePriority = prefs.priorities[aThreadPriority].realTime;
+
+ if (IsValidRealTimePriority(realTimePriority, SCHED_FIFO)) {
+ SetRealTimeThreadPriority(aTid, aThreadPriority, realTimePriority);
+ return;
+ }
+
+ SetThreadNiceValue(aTid, aThreadPriority,
+ prefs.priorities[aThreadPriority].nice);
+}
+
+namespace {
+
+/**
+ * This class sets the priority of threads given the kernel thread's id and a
+ * value taken from hal::ThreadPriority.
+ *
+ * This runnable must always be dispatched to the main thread otherwise it will fail.
+ * We have to run this from the main thread since preferences can only be read on
+ * main thread.
+ */
+class SetThreadPriorityRunnable : public Runnable
+{
+public:
+ SetThreadPriorityRunnable(pid_t aThreadId, hal::ThreadPriority aThreadPriority)
+ : mThreadId(aThreadId)
+ , mThreadPriority(aThreadPriority)
+ { }
+
+ NS_IMETHOD Run() override
+ {
+ NS_ASSERTION(NS_IsMainThread(), "Can only set thread priorities on main thread");
+ hal_impl::DoSetThreadPriority(mThreadId, mThreadPriority);
+ return NS_OK;
+ }
+
+private:
+ pid_t mThreadId;
+ hal::ThreadPriority mThreadPriority;
+};
+
+} // namespace
+
+void
+SetCurrentThreadPriority(ThreadPriority aThreadPriority)
+{
+ pid_t threadId = gettid();
+ hal_impl::SetThreadPriority(threadId, aThreadPriority);
+}
+
+void
+SetThreadPriority(PlatformThreadId aThreadId,
+ ThreadPriority aThreadPriority)
+{
+ switch (aThreadPriority) {
+ case THREAD_PRIORITY_COMPOSITOR: {
+ nsCOMPtr<nsIRunnable> runnable =
+ new SetThreadPriorityRunnable(aThreadId, aThreadPriority);
+ NS_DispatchToMainThread(runnable);
+ break;
+ }
+ default:
+ HAL_LOG("Unrecognized thread priority %d; Doing nothing",
+ aThreadPriority);
+ return;
+ }
+}
+
+void
+FactoryReset(FactoryResetReason& aReason)
+{
+ nsCOMPtr<nsIRecoveryService> recoveryService =
+ do_GetService("@mozilla.org/recovery-service;1");
+ if (!recoveryService) {
+ NS_WARNING("Could not get recovery service!");
+ return;
+ }
+
+ if (aReason == FactoryResetReason::Wipe) {
+ recoveryService->FactoryReset("wipe");
+ } else if (aReason == FactoryResetReason::Root) {
+ recoveryService->FactoryReset("root");
+ } else {
+ recoveryService->FactoryReset("normal");
+ }
+}
+
+} // hal_impl
+} // mozilla
diff --git a/hal/gonk/GonkSensor.cpp b/hal/gonk/GonkSensor.cpp
new file mode 100644
index 000000000..7bd2d3c9b
--- /dev/null
+++ b/hal/gonk/GonkSensor.cpp
@@ -0,0 +1,861 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* Copyright 2012 Mozilla Foundation and Mozilla contributors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <pthread.h>
+#include <stdio.h>
+
+#include "mozilla/DebugOnly.h"
+#include "mozilla/Saturate.h"
+
+#include "base/basictypes.h"
+#include "base/thread.h"
+#include "base/task.h"
+
+#include "GonkSensorsInterface.h"
+#include "GonkSensorsPollInterface.h"
+#include "GonkSensorsRegistryInterface.h"
+#include "Hal.h"
+#include "HalLog.h"
+#include "HalSensor.h"
+#include "hardware/sensors.h"
+#include "nsThreadUtils.h"
+
+using namespace mozilla::hal;
+
+namespace mozilla {
+
+//
+// Internal implementation
+//
+
+// The value from SensorDevice.h (Android)
+#define DEFAULT_DEVICE_POLL_RATE 200000000 /*200ms*/
+// ProcessOrientation.cpp needs smaller poll rate to detect delay between
+// different orientation angles
+#define ACCELEROMETER_POLL_RATE 66667000 /*66.667ms*/
+
+// This is present in Android from API level 18 onwards, which is 4.3. We might
+// be building on something before 4.3, so use a local define for its value
+#define MOZ_SENSOR_TYPE_GAME_ROTATION_VECTOR 15
+
+double radToDeg(double a) {
+ return a * (180.0 / M_PI);
+}
+
+static SensorType
+HardwareSensorToHalSensor(int type)
+{
+ switch(type) {
+ case SENSOR_TYPE_ORIENTATION:
+ return SENSOR_ORIENTATION;
+ case SENSOR_TYPE_ACCELEROMETER:
+ return SENSOR_ACCELERATION;
+ case SENSOR_TYPE_PROXIMITY:
+ return SENSOR_PROXIMITY;
+ case SENSOR_TYPE_LIGHT:
+ return SENSOR_LIGHT;
+ case SENSOR_TYPE_GYROSCOPE:
+ return SENSOR_GYROSCOPE;
+ case SENSOR_TYPE_LINEAR_ACCELERATION:
+ return SENSOR_LINEAR_ACCELERATION;
+ case SENSOR_TYPE_ROTATION_VECTOR:
+ return SENSOR_ROTATION_VECTOR;
+ case MOZ_SENSOR_TYPE_GAME_ROTATION_VECTOR:
+ return SENSOR_GAME_ROTATION_VECTOR;
+ default:
+ return SENSOR_UNKNOWN;
+ }
+}
+
+static SensorAccuracyType
+HardwareStatusToHalAccuracy(int status) {
+ return static_cast<SensorAccuracyType>(status);
+}
+
+static int
+HalSensorToHardwareSensor(SensorType type)
+{
+ switch(type) {
+ case SENSOR_ORIENTATION:
+ return SENSOR_TYPE_ORIENTATION;
+ case SENSOR_ACCELERATION:
+ return SENSOR_TYPE_ACCELEROMETER;
+ case SENSOR_PROXIMITY:
+ return SENSOR_TYPE_PROXIMITY;
+ case SENSOR_LIGHT:
+ return SENSOR_TYPE_LIGHT;
+ case SENSOR_GYROSCOPE:
+ return SENSOR_TYPE_GYROSCOPE;
+ case SENSOR_LINEAR_ACCELERATION:
+ return SENSOR_TYPE_LINEAR_ACCELERATION;
+ case SENSOR_ROTATION_VECTOR:
+ return SENSOR_TYPE_ROTATION_VECTOR;
+ case SENSOR_GAME_ROTATION_VECTOR:
+ return MOZ_SENSOR_TYPE_GAME_ROTATION_VECTOR;
+ default:
+ return -1;
+ }
+}
+
+static int
+SensorseventStatus(const sensors_event_t& data)
+{
+ int type = data.type;
+ switch(type) {
+ case SENSOR_ORIENTATION:
+ return data.orientation.status;
+ case SENSOR_LINEAR_ACCELERATION:
+ case SENSOR_ACCELERATION:
+ return data.acceleration.status;
+ case SENSOR_GYROSCOPE:
+ return data.gyro.status;
+ }
+
+ return SENSOR_STATUS_UNRELIABLE;
+}
+
+class SensorRunnable : public Runnable
+{
+public:
+ SensorRunnable(const sensors_event_t& data, const sensor_t* sensors, ssize_t size)
+ {
+ mSensorData.sensor() = HardwareSensorToHalSensor(data.type);
+ mSensorData.accuracy() = HardwareStatusToHalAccuracy(SensorseventStatus(data));
+ mSensorData.timestamp() = data.timestamp;
+ if (mSensorData.sensor() == SENSOR_GYROSCOPE) {
+ // libhardware returns gyro as rad. convert.
+ mSensorValues.AppendElement(radToDeg(data.data[0]));
+ mSensorValues.AppendElement(radToDeg(data.data[1]));
+ mSensorValues.AppendElement(radToDeg(data.data[2]));
+ } else if (mSensorData.sensor() == SENSOR_PROXIMITY) {
+ mSensorValues.AppendElement(data.data[0]);
+ mSensorValues.AppendElement(0);
+
+ // Determine the maxRange for this sensor.
+ for (ssize_t i = 0; i < size; i++) {
+ if (sensors[i].type == SENSOR_TYPE_PROXIMITY) {
+ mSensorValues.AppendElement(sensors[i].maxRange);
+ }
+ }
+ } else if (mSensorData.sensor() == SENSOR_LIGHT) {
+ mSensorValues.AppendElement(data.data[0]);
+ } else if (mSensorData.sensor() == SENSOR_ROTATION_VECTOR) {
+ mSensorValues.AppendElement(data.data[0]);
+ mSensorValues.AppendElement(data.data[1]);
+ mSensorValues.AppendElement(data.data[2]);
+ if (data.data[3] == 0.0) {
+ // data.data[3] was optional in Android <= API level 18. It can be computed from 012,
+ // but it's better to take the actual value if one is provided. The computation is
+ // v = 1 - d[0]*d[0] - d[1]*d[1] - d[2]*d[2]
+ // d[3] = v > 0 ? sqrt(v) : 0;
+ // I'm assuming that it will be 0 if it's not passed in. (The values form a unit
+ // quaternion, so the angle can be computed from the direction vector.)
+ float sx = data.data[0], sy = data.data[1], sz = data.data[2];
+ float v = 1.0f - sx*sx - sy*sy - sz*sz;
+ mSensorValues.AppendElement(v > 0.0f ? sqrt(v) : 0.0f);
+ } else {
+ mSensorValues.AppendElement(data.data[3]);
+ }
+ } else if (mSensorData.sensor() == SENSOR_GAME_ROTATION_VECTOR) {
+ mSensorValues.AppendElement(data.data[0]);
+ mSensorValues.AppendElement(data.data[1]);
+ mSensorValues.AppendElement(data.data[2]);
+ mSensorValues.AppendElement(data.data[3]);
+ } else {
+ mSensorValues.AppendElement(data.data[0]);
+ mSensorValues.AppendElement(data.data[1]);
+ mSensorValues.AppendElement(data.data[2]);
+ }
+ mSensorData.values() = mSensorValues;
+ }
+
+ ~SensorRunnable() {}
+
+ NS_IMETHOD Run() override
+ {
+ NotifySensorChange(mSensorData);
+ return NS_OK;
+ }
+
+private:
+ SensorData mSensorData;
+ AutoTArray<float, 4> mSensorValues;
+};
+
+namespace hal_impl {
+
+static DebugOnly<int> sSensorRefCount[NUM_SENSOR_TYPE];
+static base::Thread* sPollingThread;
+static sensors_poll_device_t* sSensorDevice;
+static sensors_module_t* sSensorModule;
+
+static void
+PollSensors()
+{
+ const size_t numEventMax = 16;
+ sensors_event_t buffer[numEventMax];
+ const sensor_t* sensors;
+ int size = sSensorModule->get_sensors_list(sSensorModule, &sensors);
+
+ do {
+ // didn't check sSensorDevice because already be done on creating pollingThread.
+ int n = sSensorDevice->poll(sSensorDevice, buffer, numEventMax);
+ if (n < 0) {
+ HAL_ERR("Error polling for sensor data (err=%d)", n);
+ break;
+ }
+
+ for (int i = 0; i < n; ++i) {
+ // FIXME: bug 802004, add proper support for the magnetic field sensor.
+ if (buffer[i].type == SENSOR_TYPE_MAGNETIC_FIELD)
+ continue;
+
+ // Bug 938035, transfer HAL data for orientation sensor to meet w3c spec
+ // ex: HAL report alpha=90 means East but alpha=90 means West in w3c spec
+ if (buffer[i].type == SENSOR_TYPE_ORIENTATION) {
+ buffer[i].orientation.azimuth = 360 - buffer[i].orientation.azimuth;
+ buffer[i].orientation.pitch = -buffer[i].orientation.pitch;
+ buffer[i].orientation.roll = -buffer[i].orientation.roll;
+ }
+
+ if (HardwareSensorToHalSensor(buffer[i].type) == SENSOR_UNKNOWN) {
+ // Emulator is broken and gives us events without types set
+ int index;
+ for (index = 0; index < size; index++) {
+ if (sensors[index].handle == buffer[i].sensor) {
+ break;
+ }
+ }
+ if (index < size &&
+ HardwareSensorToHalSensor(sensors[index].type) != SENSOR_UNKNOWN) {
+ buffer[i].type = sensors[index].type;
+ } else {
+ HAL_LOG("Could not determine sensor type of event");
+ continue;
+ }
+ }
+
+ NS_DispatchToMainThread(new SensorRunnable(buffer[i], sensors, size));
+ }
+ } while (true);
+}
+
+static void
+SwitchSensor(bool aActivate, sensor_t aSensor, pthread_t aThreadId)
+{
+ int index = HardwareSensorToHalSensor(aSensor.type);
+
+ MOZ_ASSERT(sSensorRefCount[index] || aActivate);
+
+ sSensorDevice->activate(sSensorDevice, aSensor.handle, aActivate);
+
+ if (aActivate) {
+ if (aSensor.type == SENSOR_TYPE_ACCELEROMETER) {
+ sSensorDevice->setDelay(sSensorDevice, aSensor.handle,
+ ACCELEROMETER_POLL_RATE);
+ } else {
+ sSensorDevice->setDelay(sSensorDevice, aSensor.handle,
+ DEFAULT_DEVICE_POLL_RATE);
+ }
+ }
+
+ if (aActivate) {
+ sSensorRefCount[index]++;
+ } else {
+ sSensorRefCount[index]--;
+ }
+}
+
+static void
+SetSensorState(SensorType aSensor, bool activate)
+{
+ int type = HalSensorToHardwareSensor(aSensor);
+ const sensor_t* sensors = nullptr;
+
+ int size = sSensorModule->get_sensors_list(sSensorModule, &sensors);
+ for (ssize_t i = 0; i < size; i++) {
+ if (sensors[i].type == type) {
+ SwitchSensor(activate, sensors[i], pthread_self());
+ break;
+ }
+ }
+}
+
+static void
+EnableSensorNotificationsInternal(SensorType aSensor)
+{
+ if (!sSensorModule) {
+ hw_get_module(SENSORS_HARDWARE_MODULE_ID,
+ (hw_module_t const**)&sSensorModule);
+ if (!sSensorModule) {
+ HAL_ERR("Can't get sensor HAL module\n");
+ return;
+ }
+
+ sensors_open(&sSensorModule->common, &sSensorDevice);
+ if (!sSensorDevice) {
+ sSensorModule = nullptr;
+ HAL_ERR("Can't get sensor poll device from module \n");
+ return;
+ }
+
+ sensor_t const* sensors;
+ int count = sSensorModule->get_sensors_list(sSensorModule, &sensors);
+ for (size_t i=0 ; i<size_t(count) ; i++) {
+ sSensorDevice->activate(sSensorDevice, sensors[i].handle, 0);
+ }
+ }
+
+ if (!sPollingThread) {
+ sPollingThread = new base::Thread("GonkSensors");
+ MOZ_ASSERT(sPollingThread);
+ // sPollingThread never terminates because poll may never return
+ sPollingThread->Start();
+ sPollingThread->message_loop()->PostTask(
+ NewRunnableFunction(PollSensors));
+ }
+
+ SetSensorState(aSensor, true);
+}
+
+static void
+DisableSensorNotificationsInternal(SensorType aSensor)
+{
+ if (!sSensorModule) {
+ return;
+ }
+ SetSensorState(aSensor, false);
+}
+
+//
+// Daemon
+//
+
+typedef detail::SaturateOp<uint32_t> SaturateOpUint32;
+
+/**
+ * The poll notification handler receives all events about sensors and
+ * sensor events.
+ */
+class SensorsPollNotificationHandler final
+ : public GonkSensorsPollNotificationHandler
+{
+public:
+ SensorsPollNotificationHandler(GonkSensorsPollInterface* aPollInterface)
+ : mPollInterface(aPollInterface)
+ {
+ MOZ_ASSERT(mPollInterface);
+
+ mPollInterface->SetNotificationHandler(this);
+ }
+
+ void EnableSensorsByType(SensorsType aType)
+ {
+ if (SaturateOpUint32(mClasses[aType].mActivated)++) {
+ return;
+ }
+
+ SensorsDeliveryMode deliveryMode = DefaultSensorsDeliveryMode(aType);
+
+ // Old ref-count for the sensor type was 0, so we
+ // activate all sensors of the type.
+ for (size_t i = 0; i < mSensors.Length(); ++i) {
+ if (mSensors[i].mType == aType &&
+ mSensors[i].mDeliveryMode == deliveryMode) {
+ mPollInterface->EnableSensor(mSensors[i].mId, nullptr);
+ mPollInterface->SetPeriod(mSensors[i].mId, DefaultSensorPeriod(aType),
+ nullptr);
+ }
+ }
+ }
+
+ void DisableSensorsByType(SensorsType aType)
+ {
+ if (SaturateOpUint32(mClasses[aType].mActivated)-- != 1) {
+ return;
+ }
+
+ SensorsDeliveryMode deliveryMode = DefaultSensorsDeliveryMode(aType);
+
+ // Old ref-count for the sensor type was 1, so we
+ // deactivate all sensors of the type.
+ for (size_t i = 0; i < mSensors.Length(); ++i) {
+ if (mSensors[i].mType == aType &&
+ mSensors[i].mDeliveryMode == deliveryMode) {
+ mPollInterface->DisableSensor(mSensors[i].mId, nullptr);
+ }
+ }
+ }
+
+ void ClearSensorClasses()
+ {
+ for (size_t i = 0; i < MOZ_ARRAY_LENGTH(mClasses); ++i) {
+ mClasses[i] = SensorsSensorClass();
+ }
+ }
+
+ void ClearSensors()
+ {
+ mSensors.Clear();
+ }
+
+ // Methods for SensorsPollNotificationHandler
+ //
+
+ void ErrorNotification(SensorsError aError) override
+ {
+ // XXX: Bug 1206056: Try to repair some of the errors or restart cleanly.
+ }
+
+ void SensorDetectedNotification(int32_t aId, SensorsType aType,
+ float aRange, float aResolution,
+ float aPower, int32_t aMinPeriod,
+ int32_t aMaxPeriod,
+ SensorsTriggerMode aTriggerMode,
+ SensorsDeliveryMode aDeliveryMode) override
+ {
+ auto i = FindSensorIndexById(aId);
+ if (i == -1) {
+ // Add a new sensor...
+ i = mSensors.Length();
+ mSensors.AppendElement(SensorsSensor(aId, aType, aRange, aResolution,
+ aPower, aMinPeriod, aMaxPeriod,
+ aTriggerMode, aDeliveryMode));
+ } else {
+ // ...or update an existing one.
+ mSensors[i] = SensorsSensor(aId, aType, aRange, aResolution, aPower,
+ aMinPeriod, aMaxPeriod, aTriggerMode,
+ aDeliveryMode);
+ }
+
+ mClasses[aType].UpdateFromSensor(mSensors[i]);
+
+ if (mClasses[aType].mActivated &&
+ mSensors[i].mDeliveryMode == DefaultSensorsDeliveryMode(aType)) {
+ // The new sensor's type is enabled, so enable sensor.
+ mPollInterface->EnableSensor(aId, nullptr);
+ mPollInterface->SetPeriod(mSensors[i].mId, DefaultSensorPeriod(aType),
+ nullptr);
+ }
+ }
+
+ void SensorLostNotification(int32_t aId) override
+ {
+ auto i = FindSensorIndexById(aId);
+ if (i != -1) {
+ mSensors.RemoveElementAt(i);
+ }
+ }
+
+ void EventNotification(int32_t aId, const SensorsEvent& aEvent) override
+ {
+ auto i = FindSensorIndexById(aId);
+ if (i == -1) {
+ HAL_ERR("Sensor %d not registered", aId);
+ return;
+ }
+
+ SensorData sensorData;
+ auto rv = CreateSensorData(aEvent, mClasses[mSensors[i].mType],
+ sensorData);
+ if (NS_FAILED(rv)) {
+ return;
+ }
+
+ NotifySensorChange(sensorData);
+ }
+
+private:
+ ssize_t FindSensorIndexById(int32_t aId) const
+ {
+ for (size_t i = 0; i < mSensors.Length(); ++i) {
+ if (mSensors[i].mId == aId) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ uint64_t DefaultSensorPeriod(SensorsType aType) const
+ {
+ return aType == SENSORS_TYPE_ACCELEROMETER ? ACCELEROMETER_POLL_RATE
+ : DEFAULT_DEVICE_POLL_RATE;
+ }
+
+ SensorsDeliveryMode DefaultSensorsDeliveryMode(SensorsType aType) const
+ {
+ if (aType == SENSORS_TYPE_PROXIMITY ||
+ aType == SENSORS_TYPE_SIGNIFICANT_MOTION) {
+ return SENSORS_DELIVERY_MODE_IMMEDIATE;
+ }
+ return SENSORS_DELIVERY_MODE_BEST_EFFORT;
+ }
+
+ SensorType HardwareSensorToHalSensor(SensorsType aType) const
+ {
+ // FIXME: bug 802004, add proper support for the magnetic-field sensor.
+ switch (aType) {
+ case SENSORS_TYPE_ORIENTATION:
+ return SENSOR_ORIENTATION;
+ case SENSORS_TYPE_ACCELEROMETER:
+ return SENSOR_ACCELERATION;
+ case SENSORS_TYPE_PROXIMITY:
+ return SENSOR_PROXIMITY;
+ case SENSORS_TYPE_LIGHT:
+ return SENSOR_LIGHT;
+ case SENSORS_TYPE_GYROSCOPE:
+ return SENSOR_GYROSCOPE;
+ case SENSORS_TYPE_LINEAR_ACCELERATION:
+ return SENSOR_LINEAR_ACCELERATION;
+ case SENSORS_TYPE_ROTATION_VECTOR:
+ return SENSOR_ROTATION_VECTOR;
+ case SENSORS_TYPE_GAME_ROTATION_VECTOR:
+ return SENSOR_GAME_ROTATION_VECTOR;
+ default:
+ NS_NOTREACHED("Invalid sensors type");
+ }
+ return SENSOR_UNKNOWN;
+ }
+
+ SensorAccuracyType HardwareStatusToHalAccuracy(SensorsStatus aStatus) const
+ {
+ return static_cast<SensorAccuracyType>(aStatus - 1);
+ }
+
+ nsresult CreateSensorData(const SensorsEvent& aEvent,
+ const SensorsSensorClass& aSensorClass,
+ SensorData& aSensorData) const
+ {
+ AutoTArray<float, 4> sensorValues;
+
+ auto sensor = HardwareSensorToHalSensor(aEvent.mType);
+
+ if (sensor == SENSOR_UNKNOWN) {
+ return NS_ERROR_ILLEGAL_VALUE;
+ }
+
+ aSensorData.sensor() = sensor;
+ aSensorData.accuracy() = HardwareStatusToHalAccuracy(aEvent.mStatus);
+ aSensorData.timestamp() = aEvent.mTimestamp;
+
+ if (aSensorData.sensor() == SENSOR_ORIENTATION) {
+ // Bug 938035: transfer HAL data for orientation sensor to meet W3C spec
+ // ex: HAL report alpha=90 means East but alpha=90 means West in W3C spec
+ sensorValues.AppendElement(360.0 - radToDeg(aEvent.mData.mFloat[0]));
+ sensorValues.AppendElement(-radToDeg(aEvent.mData.mFloat[1]));
+ sensorValues.AppendElement(-radToDeg(aEvent.mData.mFloat[2]));
+ } else if (aSensorData.sensor() == SENSOR_ACCELERATION) {
+ sensorValues.AppendElement(aEvent.mData.mFloat[0]);
+ sensorValues.AppendElement(aEvent.mData.mFloat[1]);
+ sensorValues.AppendElement(aEvent.mData.mFloat[2]);
+ } else if (aSensorData.sensor() == SENSOR_PROXIMITY) {
+ sensorValues.AppendElement(aEvent.mData.mFloat[0]);
+ sensorValues.AppendElement(aSensorClass.mMinValue);
+ sensorValues.AppendElement(aSensorClass.mMaxValue);
+ } else if (aSensorData.sensor() == SENSOR_LINEAR_ACCELERATION) {
+ sensorValues.AppendElement(aEvent.mData.mFloat[0]);
+ sensorValues.AppendElement(aEvent.mData.mFloat[1]);
+ sensorValues.AppendElement(aEvent.mData.mFloat[2]);
+ } else if (aSensorData.sensor() == SENSOR_GYROSCOPE) {
+ sensorValues.AppendElement(radToDeg(aEvent.mData.mFloat[0]));
+ sensorValues.AppendElement(radToDeg(aEvent.mData.mFloat[1]));
+ sensorValues.AppendElement(radToDeg(aEvent.mData.mFloat[2]));
+ } else if (aSensorData.sensor() == SENSOR_LIGHT) {
+ sensorValues.AppendElement(aEvent.mData.mFloat[0]);
+ } else if (aSensorData.sensor() == SENSOR_ROTATION_VECTOR) {
+ sensorValues.AppendElement(aEvent.mData.mFloat[0]);
+ sensorValues.AppendElement(aEvent.mData.mFloat[1]);
+ sensorValues.AppendElement(aEvent.mData.mFloat[2]);
+ sensorValues.AppendElement(aEvent.mData.mFloat[3]);
+ } else if (aSensorData.sensor() == SENSOR_GAME_ROTATION_VECTOR) {
+ sensorValues.AppendElement(aEvent.mData.mFloat[0]);
+ sensorValues.AppendElement(aEvent.mData.mFloat[1]);
+ sensorValues.AppendElement(aEvent.mData.mFloat[2]);
+ sensorValues.AppendElement(aEvent.mData.mFloat[3]);
+ }
+
+ aSensorData.values() = sensorValues;
+
+ return NS_OK;
+ }
+
+ GonkSensorsPollInterface* mPollInterface;
+ nsTArray<SensorsSensor> mSensors;
+ SensorsSensorClass mClasses[SENSORS_NUM_TYPES];
+};
+
+static StaticAutoPtr<SensorsPollNotificationHandler> sPollNotificationHandler;
+
+/**
+ * This is the notifiaction handler for the Sensors interface. If the backend
+ * crashes, we can restart it from here.
+ */
+class SensorsNotificationHandler final : public GonkSensorsNotificationHandler
+{
+public:
+ SensorsNotificationHandler(GonkSensorsInterface* aInterface)
+ : mInterface(aInterface)
+ {
+ MOZ_ASSERT(mInterface);
+
+ mInterface->SetNotificationHandler(this);
+ }
+
+ void BackendErrorNotification(bool aCrashed) override
+ {
+ // XXX: Bug 1206056: restart sensorsd
+ }
+
+private:
+ GonkSensorsInterface* mInterface;
+};
+
+static StaticAutoPtr<SensorsNotificationHandler> sNotificationHandler;
+
+/**
+ * |SensorsRegisterModuleResultHandler| implements the result-handler
+ * callback for registering the Poll service and activating the first
+ * sensors. If an error occures during the process, the result handler
+ * disconnects and closes the backend.
+ */
+class SensorsRegisterModuleResultHandler final
+ : public GonkSensorsRegistryResultHandler
+{
+public:
+ SensorsRegisterModuleResultHandler(
+ uint32_t* aSensorsTypeActivated,
+ GonkSensorsInterface* aInterface)
+ : mSensorsTypeActivated(aSensorsTypeActivated)
+ , mInterface(aInterface)
+ {
+ MOZ_ASSERT(mSensorsTypeActivated);
+ MOZ_ASSERT(mInterface);
+ }
+ void OnError(SensorsError aError) override
+ {
+ GonkSensorsRegistryResultHandler::OnError(aError); // print error message
+ Disconnect(); // Registering failed, so close the connection completely
+ }
+ void RegisterModule(uint32_t aProtocolVersion) override
+ {
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(!sPollNotificationHandler);
+
+ // Init, step 3: set notification handler for poll service and vice versa
+ auto pollInterface = mInterface->GetSensorsPollInterface();
+ if (!pollInterface) {
+ Disconnect();
+ return;
+ }
+ if (NS_FAILED(pollInterface->SetProtocolVersion(aProtocolVersion))) {
+ Disconnect();
+ return;
+ }
+
+ sPollNotificationHandler =
+ new SensorsPollNotificationHandler(pollInterface);
+
+ // Init, step 4: activate sensors
+ for (int i = 0; i < SENSORS_NUM_TYPES; ++i) {
+ while (mSensorsTypeActivated[i]) {
+ sPollNotificationHandler->EnableSensorsByType(
+ static_cast<SensorsType>(i));
+ --mSensorsTypeActivated[i];
+ }
+ }
+ }
+public:
+ void Disconnect()
+ {
+ class DisconnectResultHandler final : public GonkSensorsResultHandler
+ {
+ public:
+ void OnError(SensorsError aError)
+ {
+ GonkSensorsResultHandler::OnError(aError); // print error message
+ sNotificationHandler = nullptr;
+ }
+ void Disconnect() override
+ {
+ sNotificationHandler = nullptr;
+ }
+ };
+ mInterface->Disconnect(new DisconnectResultHandler());
+ }
+private:
+ uint32_t* mSensorsTypeActivated;
+ GonkSensorsInterface* mInterface;
+};
+
+/**
+ * |SensorsConnectResultHandler| implements the result-handler
+ * callback for starting the Sensors backend.
+ */
+class SensorsConnectResultHandler final : public GonkSensorsResultHandler
+{
+public:
+ SensorsConnectResultHandler(
+ uint32_t* aSensorsTypeActivated,
+ GonkSensorsInterface* aInterface)
+ : mSensorsTypeActivated(aSensorsTypeActivated)
+ , mInterface(aInterface)
+ {
+ MOZ_ASSERT(mSensorsTypeActivated);
+ MOZ_ASSERT(mInterface);
+ }
+ void OnError(SensorsError aError) override
+ {
+ GonkSensorsResultHandler::OnError(aError); // print error message
+ sNotificationHandler = nullptr;
+ }
+ void Connect() override
+ {
+ MOZ_ASSERT(NS_IsMainThread());
+
+ // Init, step 2: register poll service
+ auto registryInterface = mInterface->GetSensorsRegistryInterface();
+ if (!registryInterface) {
+ return;
+ }
+ registryInterface->RegisterModule(
+ GonkSensorsPollModule::SERVICE_ID,
+ new SensorsRegisterModuleResultHandler(mSensorsTypeActivated,
+ mInterface));
+ }
+private:
+ uint32_t* mSensorsTypeActivated;
+ GonkSensorsInterface* mInterface;
+};
+
+static uint32_t sSensorsTypeActivated[SENSORS_NUM_TYPES];
+
+static const SensorsType sSensorsType[] = {
+ [SENSOR_ORIENTATION] = SENSORS_TYPE_ORIENTATION,
+ [SENSOR_ACCELERATION] = SENSORS_TYPE_ACCELEROMETER,
+ [SENSOR_PROXIMITY] = SENSORS_TYPE_PROXIMITY,
+ [SENSOR_LINEAR_ACCELERATION] = SENSORS_TYPE_LINEAR_ACCELERATION,
+ [SENSOR_GYROSCOPE] = SENSORS_TYPE_GYROSCOPE,
+ [SENSOR_LIGHT] = SENSORS_TYPE_LIGHT,
+ [SENSOR_ROTATION_VECTOR] = SENSORS_TYPE_ROTATION_VECTOR,
+ [SENSOR_GAME_ROTATION_VECTOR] = SENSORS_TYPE_GAME_ROTATION_VECTOR
+};
+
+void
+EnableSensorNotificationsDaemon(SensorType aSensor)
+{
+ if ((aSensor < 0) ||
+ (aSensor > static_cast<ssize_t>(MOZ_ARRAY_LENGTH(sSensorsType)))) {
+ HAL_ERR("Sensor type %d not known", aSensor);
+ return; // Unsupported sensor type
+ }
+
+ auto interface = GonkSensorsInterface::GetInstance();
+ if (!interface) {
+ return;
+ }
+
+ if (sPollNotificationHandler) {
+ // Everythings already up and running; enable sensor type.
+ sPollNotificationHandler->EnableSensorsByType(sSensorsType[aSensor]);
+ return;
+ }
+
+ ++SaturateOpUint32(sSensorsTypeActivated[sSensorsType[aSensor]]);
+
+ if (sNotificationHandler) {
+ // We are in the middle of a pending start up; nothing else to do.
+ return;
+ }
+
+ // Start up
+
+ MOZ_ASSERT(!sPollNotificationHandler);
+ MOZ_ASSERT(!sNotificationHandler);
+
+ sNotificationHandler = new SensorsNotificationHandler(interface);
+
+ // Init, step 1: connect to Sensors backend
+ interface->Connect(
+ sNotificationHandler,
+ new SensorsConnectResultHandler(sSensorsTypeActivated, interface));
+}
+
+void
+DisableSensorNotificationsDaemon(SensorType aSensor)
+{
+ if ((aSensor < 0) ||
+ (aSensor > static_cast<ssize_t>(MOZ_ARRAY_LENGTH(sSensorsType)))) {
+ HAL_ERR("Sensor type %d not known", aSensor);
+ return; // Unsupported sensor type
+ }
+
+ if (sPollNotificationHandler) {
+ // Everthings up and running; disable sensors type
+ sPollNotificationHandler->DisableSensorsByType(sSensorsType[aSensor]);
+ return;
+ }
+
+ // We might be in the middle of a startup; decrement type's ref-counter.
+ --SaturateOpUint32(sSensorsTypeActivated[sSensorsType[aSensor]]);
+
+ // TODO: stop sensorsd if all sensors are disabled
+}
+
+//
+// Public interface
+//
+
+// TODO: Remove in-Gecko sensors code. Until all devices' base
+// images come with sensorsd installed, we have to support the
+// in-Gecko implementation as well. So we test for the existance
+// of the binary. If it's there, we use it. Otherwise we run the
+// old code.
+static bool
+HasDaemon()
+{
+ static bool tested;
+ static bool hasDaemon;
+
+ if (MOZ_UNLIKELY(!tested)) {
+ hasDaemon = !access("/system/bin/sensorsd", X_OK);
+ tested = true;
+ }
+
+ return hasDaemon;
+}
+
+void
+EnableSensorNotifications(SensorType aSensor)
+{
+ if (HasDaemon()) {
+ EnableSensorNotificationsDaemon(aSensor);
+ } else {
+ EnableSensorNotificationsInternal(aSensor);
+ }
+}
+
+void
+DisableSensorNotifications(SensorType aSensor)
+{
+ if (HasDaemon()) {
+ DisableSensorNotificationsDaemon(aSensor);
+ } else {
+ DisableSensorNotificationsInternal(aSensor);
+ }
+}
+
+} // hal_impl
+} // mozilla
diff --git a/hal/gonk/GonkSensorsHelpers.cpp b/hal/gonk/GonkSensorsHelpers.cpp
new file mode 100644
index 000000000..ccc940c7c
--- /dev/null
+++ b/hal/gonk/GonkSensorsHelpers.cpp
@@ -0,0 +1,112 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 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 "GonkSensorsHelpers.h"
+
+namespace mozilla {
+namespace hal {
+
+//
+// Unpacking
+//
+
+nsresult
+UnpackPDU(DaemonSocketPDU& aPDU, SensorsEvent& aOut)
+{
+ nsresult rv = UnpackPDU(aPDU, aOut.mType);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ rv = UnpackPDU(aPDU, aOut.mTimestamp);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ rv = UnpackPDU(aPDU, aOut.mStatus);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ size_t i = 0;
+
+ switch (aOut.mType) {
+ case SENSORS_TYPE_MAGNETIC_FIELD_UNCALIBRATED:
+ case SENSORS_TYPE_GYROSCOPE_UNCALIBRATED:
+ /* 6 data values */
+ rv = UnpackPDU(aPDU, aOut.mData.mFloat[i++]);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ /* fall through */
+ case SENSORS_TYPE_ROTATION_VECTOR:
+ case SENSORS_TYPE_GAME_ROTATION_VECTOR:
+ case SENSORS_TYPE_GEOMAGNETIC_ROTATION_VECTOR:
+ /* 5 data values */
+ rv = UnpackPDU(aPDU, aOut.mData.mFloat[i++]);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ rv = UnpackPDU(aPDU, aOut.mData.mFloat[i++]);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ /* fall through */
+ case SENSORS_TYPE_ACCELEROMETER:
+ case SENSORS_TYPE_GEOMAGNETIC_FIELD:
+ case SENSORS_TYPE_ORIENTATION:
+ case SENSORS_TYPE_GYROSCOPE:
+ case SENSORS_TYPE_GRAVITY:
+ case SENSORS_TYPE_LINEAR_ACCELERATION:
+ /* 3 data values */
+ rv = UnpackPDU(aPDU, aOut.mData.mFloat[i++]);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ rv = UnpackPDU(aPDU, aOut.mData.mFloat[i++]);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ /* fall through */
+ case SENSORS_TYPE_LIGHT:
+ case SENSORS_TYPE_PRESSURE:
+ case SENSORS_TYPE_TEMPERATURE:
+ case SENSORS_TYPE_PROXIMITY:
+ case SENSORS_TYPE_RELATIVE_HUMIDITY:
+ case SENSORS_TYPE_AMBIENT_TEMPERATURE:
+ case SENSORS_TYPE_HEART_RATE:
+ case SENSORS_TYPE_TILT_DETECTOR:
+ case SENSORS_TYPE_WAKE_GESTURE:
+ case SENSORS_TYPE_GLANCE_GESTURE:
+ case SENSORS_TYPE_PICK_UP_GESTURE:
+ case SENSORS_TYPE_WRIST_TILT_GESTURE:
+ case SENSORS_TYPE_SIGNIFICANT_MOTION:
+ case SENSORS_TYPE_STEP_DETECTED:
+ /* 1 data value */
+ rv = UnpackPDU(aPDU, aOut.mData.mFloat[i++]);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ break;
+ case SENSORS_TYPE_STEP_COUNTER:
+ /* 1 data value */
+ rv = UnpackPDU(aPDU, aOut.mData.mUint[0]);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ break;
+ default:
+ if (MOZ_HAL_IPC_UNPACK_WARN_IF(true, SensorsEvent)) {
+ return NS_ERROR_ILLEGAL_VALUE;
+ }
+ }
+ rv = UnpackPDU(aPDU, aOut.mDeliveryMode);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ return NS_OK;
+}
+
+} // namespace hal
+} // namespace mozilla
diff --git a/hal/gonk/GonkSensorsHelpers.h b/hal/gonk/GonkSensorsHelpers.h
new file mode 100644
index 000000000..5218af53a
--- /dev/null
+++ b/hal/gonk/GonkSensorsHelpers.h
@@ -0,0 +1,226 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 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 hal_gonk_GonkSensorsHelpers_h
+#define hal_gonk_GonkSensorsHelpers_h
+
+#include <mozilla/ipc/DaemonSocketPDU.h>
+#include <mozilla/ipc/DaemonSocketPDUHelpers.h>
+#include "SensorsTypes.h"
+
+namespace mozilla {
+namespace hal {
+
+using mozilla::ipc::DaemonSocketPDU;
+using mozilla::ipc::DaemonSocketPDUHeader;
+using mozilla::ipc::DaemonSocketPDUHelpers::Convert;
+using mozilla::ipc::DaemonSocketPDUHelpers::PackPDU;
+using mozilla::ipc::DaemonSocketPDUHelpers::UnpackPDU;
+
+using namespace mozilla::ipc::DaemonSocketPDUHelpers;
+
+//
+// Conversion
+//
+// The functions below convert the input value to the output value's
+// type and perform extension tests on the validity of the result. On
+// success the output value will be returned in |aOut|. The functions
+// return NS_OK on success, or an XPCOM error code otherwise.
+//
+// See the documentation of the HAL IPC framework for more information
+// on conversion functions.
+//
+
+nsresult
+Convert(int32_t aIn, SensorsStatus& aOut)
+{
+ static const uint8_t sStatus[] = {
+ [0] = SENSORS_STATUS_NO_CONTACT, // '-1'
+ [1] = SENSORS_STATUS_UNRELIABLE, // '0'
+ [2] = SENSORS_STATUS_ACCURACY_LOW, // '1'
+ [3] = SENSORS_STATUS_ACCURACY_MEDIUM, // '2'
+ [4] = SENSORS_STATUS_ACCURACY_HIGH // '3'
+ };
+ static const int8_t sOffset = -1; // '-1' is the lower bound of the status
+
+ if (MOZ_HAL_IPC_CONVERT_WARN_IF(aIn < sOffset, int32_t, SensorsStatus) ||
+ MOZ_HAL_IPC_CONVERT_WARN_IF(
+ aIn >= (static_cast<ssize_t>(MOZ_ARRAY_LENGTH(sStatus)) + sOffset),
+ int32_t, SensorsStatus)) {
+ return NS_ERROR_ILLEGAL_VALUE;
+ }
+ aOut = static_cast<SensorsStatus>(sStatus[aIn - sOffset]);
+ return NS_OK;
+}
+
+nsresult
+Convert(uint8_t aIn, SensorsDeliveryMode& aOut)
+{
+ static const uint8_t sMode[] = {
+ [0x00] = SENSORS_DELIVERY_MODE_BEST_EFFORT,
+ [0x01] = SENSORS_DELIVERY_MODE_IMMEDIATE
+ };
+ if (MOZ_HAL_IPC_CONVERT_WARN_IF(
+ aIn >= MOZ_ARRAY_LENGTH(sMode), uint8_t, SensorsDeliveryMode)) {
+ return NS_ERROR_ILLEGAL_VALUE;
+ }
+ aOut = static_cast<SensorsDeliveryMode>(sMode[aIn]);
+ return NS_OK;
+}
+
+nsresult
+Convert(uint8_t aIn, SensorsError& aOut)
+{
+ static const uint8_t sError[] = {
+ [0x00] = SENSORS_ERROR_NONE,
+ [0x01] = SENSORS_ERROR_FAIL,
+ [0x02] = SENSORS_ERROR_NOT_READY,
+ [0x03] = SENSORS_ERROR_NOMEM,
+ [0x04] = SENSORS_ERROR_BUSY,
+ [0x05] = SENSORS_ERROR_DONE,
+ [0x06] = SENSORS_ERROR_UNSUPPORTED,
+ [0x07] = SENSORS_ERROR_PARM_INVALID
+ };
+ if (MOZ_HAL_IPC_CONVERT_WARN_IF(
+ aIn >= MOZ_ARRAY_LENGTH(sError), uint8_t, SensorsError)) {
+ return NS_ERROR_ILLEGAL_VALUE;
+ }
+ aOut = static_cast<SensorsError>(sError[aIn]);
+ return NS_OK;
+}
+
+nsresult
+Convert(uint8_t aIn, SensorsTriggerMode& aOut)
+{
+ static const uint8_t sMode[] = {
+ [0x00] = SENSORS_TRIGGER_MODE_CONTINUOUS,
+ [0x01] = SENSORS_TRIGGER_MODE_ON_CHANGE,
+ [0x02] = SENSORS_TRIGGER_MODE_ONE_SHOT,
+ [0x03] = SENSORS_TRIGGER_MODE_SPECIAL
+ };
+ if (MOZ_HAL_IPC_CONVERT_WARN_IF(
+ aIn >= MOZ_ARRAY_LENGTH(sMode), uint8_t, SensorsTriggerMode)) {
+ return NS_ERROR_ILLEGAL_VALUE;
+ }
+ aOut = static_cast<SensorsTriggerMode>(sMode[aIn]);
+ return NS_OK;
+}
+
+nsresult
+Convert(uint32_t aIn, SensorsType& aOut)
+{
+ static const uint8_t sType[] = {
+ [0x00] = 0, // invalid, required by gcc
+ [0x01] = SENSORS_TYPE_ACCELEROMETER,
+ [0x02] = SENSORS_TYPE_GEOMAGNETIC_FIELD,
+ [0x03] = SENSORS_TYPE_ORIENTATION,
+ [0x04] = SENSORS_TYPE_GYROSCOPE,
+ [0x05] = SENSORS_TYPE_LIGHT,
+ [0x06] = SENSORS_TYPE_PRESSURE,
+ [0x07] = SENSORS_TYPE_TEMPERATURE,
+ [0x08] = SENSORS_TYPE_PROXIMITY,
+ [0x09] = SENSORS_TYPE_GRAVITY,
+ [0x0a] = SENSORS_TYPE_LINEAR_ACCELERATION,
+ [0x0b] = SENSORS_TYPE_ROTATION_VECTOR,
+ [0x0c] = SENSORS_TYPE_RELATIVE_HUMIDITY,
+ [0x0d] = SENSORS_TYPE_AMBIENT_TEMPERATURE,
+ [0x0e] = SENSORS_TYPE_MAGNETIC_FIELD_UNCALIBRATED,
+ [0x0f] = SENSORS_TYPE_GAME_ROTATION_VECTOR,
+ [0x10] = SENSORS_TYPE_GYROSCOPE_UNCALIBRATED,
+ [0x11] = SENSORS_TYPE_SIGNIFICANT_MOTION,
+ [0x12] = SENSORS_TYPE_STEP_DETECTED,
+ [0x13] = SENSORS_TYPE_STEP_COUNTER,
+ [0x14] = SENSORS_TYPE_GEOMAGNETIC_ROTATION_VECTOR,
+ [0x15] = SENSORS_TYPE_HEART_RATE,
+ [0x16] = SENSORS_TYPE_TILT_DETECTOR,
+ [0x17] = SENSORS_TYPE_WAKE_GESTURE,
+ [0x18] = SENSORS_TYPE_GLANCE_GESTURE,
+ [0x19] = SENSORS_TYPE_PICK_UP_GESTURE,
+ [0x1a] = SENSORS_TYPE_WRIST_TILT_GESTURE
+ };
+ if (MOZ_HAL_IPC_CONVERT_WARN_IF(
+ !aIn, uint32_t, SensorsType) ||
+ MOZ_HAL_IPC_CONVERT_WARN_IF(
+ aIn >= MOZ_ARRAY_LENGTH(sType), uint32_t, SensorsType)) {
+ return NS_ERROR_ILLEGAL_VALUE;
+ }
+ aOut = static_cast<SensorsType>(sType[aIn]);
+ return NS_OK;
+}
+
+nsresult
+Convert(nsresult aIn, SensorsError& aOut)
+{
+ if (NS_SUCCEEDED(aIn)) {
+ aOut = SENSORS_ERROR_NONE;
+ } else if (aIn == NS_ERROR_OUT_OF_MEMORY) {
+ aOut = SENSORS_ERROR_NOMEM;
+ } else if (aIn == NS_ERROR_ILLEGAL_VALUE) {
+ aOut = SENSORS_ERROR_PARM_INVALID;
+ } else {
+ aOut = SENSORS_ERROR_FAIL;
+ }
+ return NS_OK;
+}
+
+//
+// Packing
+//
+// Pack functions store a value in PDU. See the documentation of the
+// HAL IPC framework for more information.
+//
+// There are currently no sensor-specific pack functions necessary. If
+// you add one, put it below.
+//
+
+//
+// Unpacking
+//
+// Unpack function retrieve a value from a PDU. The functions return
+// NS_OK on success, or an XPCOM error code otherwise. On sucess, the
+// returned value is stored in the second argument |aOut|.
+//
+// See the documentation of the HAL IPC framework for more information
+// on unpack functions.
+//
+
+nsresult
+UnpackPDU(DaemonSocketPDU& aPDU, SensorsDeliveryMode& aOut)
+{
+ return UnpackPDU(aPDU, UnpackConversion<uint8_t, SensorsDeliveryMode>(aOut));
+}
+
+nsresult
+UnpackPDU(DaemonSocketPDU& aPDU, SensorsError& aOut)
+{
+ return UnpackPDU(aPDU, UnpackConversion<uint8_t, SensorsError>(aOut));
+}
+
+nsresult
+UnpackPDU(DaemonSocketPDU& aPDU, SensorsEvent& aOut);
+
+nsresult
+UnpackPDU(DaemonSocketPDU& aPDU, SensorsStatus& aOut)
+{
+ return UnpackPDU(aPDU, UnpackConversion<int32_t, SensorsStatus>(aOut));
+}
+
+nsresult
+UnpackPDU(DaemonSocketPDU& aPDU, SensorsTriggerMode& aOut)
+{
+ return UnpackPDU(aPDU, UnpackConversion<uint8_t, SensorsTriggerMode>(aOut));
+}
+
+nsresult
+UnpackPDU(DaemonSocketPDU& aPDU, SensorsType& aOut)
+{
+ return UnpackPDU(aPDU, UnpackConversion<uint32_t, SensorsType>(aOut));
+}
+
+} // namespace hal
+} // namespace mozilla
+
+#endif // hal_gonk_GonkSensorsHelpers_h
diff --git a/hal/gonk/GonkSensorsInterface.cpp b/hal/gonk/GonkSensorsInterface.cpp
new file mode 100644
index 000000000..51e1ff50c
--- /dev/null
+++ b/hal/gonk/GonkSensorsInterface.cpp
@@ -0,0 +1,494 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 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 "GonkSensorsInterface.h"
+#include "GonkSensorsPollInterface.h"
+#include "GonkSensorsRegistryInterface.h"
+#include "HalLog.h"
+#include <mozilla/ipc/DaemonSocket.h>
+#include <mozilla/ipc/DaemonSocketConnector.h>
+#include <mozilla/ipc/ListenSocket.h>
+
+namespace mozilla {
+namespace hal {
+
+using namespace mozilla::ipc;
+
+//
+// GonkSensorsResultHandler
+//
+
+void
+GonkSensorsResultHandler::OnError(SensorsError aError)
+{
+ HAL_ERR("Received error code %d", static_cast<int>(aError));
+}
+
+void
+GonkSensorsResultHandler::Connect()
+{ }
+
+void
+GonkSensorsResultHandler::Disconnect()
+{ }
+
+GonkSensorsResultHandler::~GonkSensorsResultHandler()
+{ }
+
+//
+// GonkSensorsNotificationHandler
+//
+
+void
+GonkSensorsNotificationHandler::BackendErrorNotification(bool aCrashed)
+{
+ if (aCrashed) {
+ HAL_ERR("Sensors backend crashed");
+ } else {
+ HAL_ERR("Error in sensors backend");
+ }
+}
+
+GonkSensorsNotificationHandler::~GonkSensorsNotificationHandler()
+{ }
+
+//
+// GonkSensorsProtocol
+//
+
+class GonkSensorsProtocol final
+ : public DaemonSocketIOConsumer
+ , public GonkSensorsRegistryModule
+ , public GonkSensorsPollModule
+{
+public:
+ GonkSensorsProtocol();
+
+ void SetConnection(DaemonSocket* aConnection);
+
+ already_AddRefed<DaemonSocketResultHandler> FetchResultHandler(
+ const DaemonSocketPDUHeader& aHeader);
+
+ // Methods for |SensorsRegistryModule| and |SensorsPollModule|
+ //
+
+ nsresult Send(DaemonSocketPDU* aPDU,
+ DaemonSocketResultHandler* aRes) override;
+
+ // Methods for |DaemonSocketIOConsumer|
+ //
+
+ void Handle(DaemonSocketPDU& aPDU) override;
+ void StoreResultHandler(const DaemonSocketPDU& aPDU) override;
+
+private:
+ void HandleRegistrySvc(const DaemonSocketPDUHeader& aHeader,
+ DaemonSocketPDU& aPDU,
+ DaemonSocketResultHandler* aRes);
+ void HandlePollSvc(const DaemonSocketPDUHeader& aHeader,
+ DaemonSocketPDU& aPDU,
+ DaemonSocketResultHandler* aRes);
+
+ DaemonSocket* mConnection;
+ nsTArray<RefPtr<DaemonSocketResultHandler>> mResultHandlerQ;
+};
+
+GonkSensorsProtocol::GonkSensorsProtocol()
+{ }
+
+void
+GonkSensorsProtocol::SetConnection(DaemonSocket* aConnection)
+{
+ mConnection = aConnection;
+}
+
+already_AddRefed<DaemonSocketResultHandler>
+GonkSensorsProtocol::FetchResultHandler(const DaemonSocketPDUHeader& aHeader)
+{
+ MOZ_ASSERT(!NS_IsMainThread());
+
+ if (aHeader.mOpcode & 0x80) {
+ return nullptr; // Ignore notifications
+ }
+
+ RefPtr<DaemonSocketResultHandler> res = mResultHandlerQ.ElementAt(0);
+ mResultHandlerQ.RemoveElementAt(0);
+
+ return res.forget();
+}
+
+void
+GonkSensorsProtocol::HandleRegistrySvc(
+ const DaemonSocketPDUHeader& aHeader, DaemonSocketPDU& aPDU,
+ DaemonSocketResultHandler* aRes)
+{
+ GonkSensorsRegistryModule::HandleSvc(aHeader, aPDU, aRes);
+}
+
+void
+GonkSensorsProtocol::HandlePollSvc(
+ const DaemonSocketPDUHeader& aHeader, DaemonSocketPDU& aPDU,
+ DaemonSocketResultHandler* aRes)
+{
+ GonkSensorsPollModule::HandleSvc(aHeader, aPDU, aRes);
+}
+
+// |SensorsRegistryModule|, |SensorsPollModule|
+
+nsresult
+GonkSensorsProtocol::Send(DaemonSocketPDU* aPDU,
+ DaemonSocketResultHandler* aRes)
+{
+ MOZ_ASSERT(mConnection);
+ MOZ_ASSERT(aPDU);
+
+ aPDU->SetConsumer(this);
+ aPDU->SetResultHandler(aRes);
+ aPDU->UpdateHeader();
+
+ if (mConnection->GetConnectionStatus() == SOCKET_DISCONNECTED) {
+ HAL_ERR("Sensors socket is disconnected");
+ return NS_ERROR_FAILURE;
+ }
+
+ mConnection->SendSocketData(aPDU); // Forward PDU to data channel
+
+ return NS_OK;
+}
+
+// |DaemonSocketIOConsumer|
+
+void
+GonkSensorsProtocol::Handle(DaemonSocketPDU& aPDU)
+{
+ static void (GonkSensorsProtocol::* const HandleSvc[])(
+ const DaemonSocketPDUHeader&, DaemonSocketPDU&,
+ DaemonSocketResultHandler*) = {
+ [GonkSensorsRegistryModule::SERVICE_ID] =
+ &GonkSensorsProtocol::HandleRegistrySvc,
+ [GonkSensorsPollModule::SERVICE_ID] =
+ &GonkSensorsProtocol::HandlePollSvc
+ };
+
+ DaemonSocketPDUHeader header;
+
+ if (NS_FAILED(UnpackPDU(aPDU, header))) {
+ return;
+ }
+ if (!(header.mService < MOZ_ARRAY_LENGTH(HandleSvc)) ||
+ !HandleSvc[header.mService]) {
+ HAL_ERR("Sensors service %d unknown", header.mService);
+ return;
+ }
+
+ RefPtr<DaemonSocketResultHandler> res = FetchResultHandler(header);
+
+ (this->*(HandleSvc[header.mService]))(header, aPDU, res);
+}
+
+void
+GonkSensorsProtocol::StoreResultHandler(const DaemonSocketPDU& aPDU)
+{
+ MOZ_ASSERT(!NS_IsMainThread());
+
+ mResultHandlerQ.AppendElement(aPDU.GetResultHandler());
+}
+
+//
+// GonkSensorsInterface
+//
+
+GonkSensorsInterface*
+GonkSensorsInterface::GetInstance()
+{
+ static GonkSensorsInterface* sGonkSensorsInterface;
+
+ if (sGonkSensorsInterface) {
+ return sGonkSensorsInterface;
+ }
+
+ sGonkSensorsInterface = new GonkSensorsInterface();
+
+ return sGonkSensorsInterface;
+}
+
+void
+GonkSensorsInterface::SetNotificationHandler(
+ GonkSensorsNotificationHandler* aNotificationHandler)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+
+ mNotificationHandler = aNotificationHandler;
+}
+
+/*
+ * The connect procedure consists of several steps.
+ *
+ * (1) Start listening for the command channel's socket connection: We
+ * do this before anything else, so that we don't miss connection
+ * requests from the Sensors daemon. This step will create a listen
+ * socket.
+ *
+ * (2) Start the Sensors daemon: When the daemon starts up it will open
+ * a socket connection to Gecko and thus create the data channel.
+ * Gecko already opened the listen socket in step (1). Step (2) ends
+ * with the creation of the data channel.
+ *
+ * (3) Signal success to the caller.
+ *
+ * If any step fails, we roll-back the procedure and signal an error to the
+ * caller.
+ */
+void
+GonkSensorsInterface::Connect(GonkSensorsNotificationHandler* aNotificationHandler,
+ GonkSensorsResultHandler* aRes)
+{
+#define BASE_SOCKET_NAME "sensorsd"
+ static unsigned long POSTFIX_LENGTH = 16;
+
+ // If we could not cleanup properly before and an old
+ // instance of the daemon is still running, we kill it
+ // here.
+ mozilla::hal::StopSystemService("sensorsd");
+
+ mNotificationHandler = aNotificationHandler;
+
+ mResultHandlerQ.AppendElement(aRes);
+
+ if (!mProtocol) {
+ mProtocol = MakeUnique<GonkSensorsProtocol>();
+ }
+
+ if (!mListenSocket) {
+ mListenSocket = new ListenSocket(this, LISTEN_SOCKET);
+ }
+
+ // Init, step 1: Listen for data channel... */
+
+ if (!mDataSocket) {
+ mDataSocket = new DaemonSocket(mProtocol.get(), this, DATA_SOCKET);
+ } else if (mDataSocket->GetConnectionStatus() == SOCKET_CONNECTED) {
+ // Command channel should not be open; let's close it.
+ mDataSocket->Close();
+ }
+
+ // The listen socket's name is generated with a random postfix. This
+ // avoids naming collisions if we still have a listen socket from a
+ // previously failed cleanup. It also makes it hard for malicious
+ // external programs to capture the socket name or connect before
+ // the daemon can do so. If no random postfix can be generated, we
+ // simply use the base name as-is.
+ nsresult rv = DaemonSocketConnector::CreateRandomAddressString(
+ NS_LITERAL_CSTRING(BASE_SOCKET_NAME), POSTFIX_LENGTH, mListenSocketName);
+ if (NS_FAILED(rv)) {
+ mListenSocketName.AssignLiteral(BASE_SOCKET_NAME);
+ }
+
+ rv = mListenSocket->Listen(new DaemonSocketConnector(mListenSocketName),
+ mDataSocket);
+ if (NS_FAILED(rv)) {
+ OnConnectError(DATA_SOCKET);
+ return;
+ }
+
+ // The protocol implementation needs a data channel for
+ // sending commands to the daemon. We set it here, because
+ // this is the earliest time when it's available.
+ mProtocol->SetConnection(mDataSocket);
+}
+
+/*
+ * Disconnecting is inverse to connecting.
+ *
+ * (1) Close data socket: We close the data channel and the daemon will
+ * will notice. Once we see the socket's disconnect, we continue with
+ * the cleanup.
+ *
+ * (2) Close listen socket: The listen socket is not active any longer
+ * and we simply close it.
+ *
+ * (3) Signal success to the caller.
+ *
+ * We don't have to stop the daemon explicitly. It will cleanup and quit
+ * after it noticed the closing of the data channel
+ *
+ * Rolling back half-completed cleanups is not possible. In the case of
+ * an error, we simply push forward and try to recover during the next
+ * initialization.
+ */
+void
+GonkSensorsInterface::Disconnect(GonkSensorsResultHandler* aRes)
+{
+ mNotificationHandler = nullptr;
+
+ // Cleanup, step 1: Close data channel
+ mDataSocket->Close();
+
+ mResultHandlerQ.AppendElement(aRes);
+}
+
+GonkSensorsRegistryInterface*
+GonkSensorsInterface::GetSensorsRegistryInterface()
+{
+ if (mRegistryInterface) {
+ return mRegistryInterface.get();
+ }
+
+ mRegistryInterface = MakeUnique<GonkSensorsRegistryInterface>(mProtocol.get());
+
+ return mRegistryInterface.get();
+}
+
+GonkSensorsPollInterface*
+GonkSensorsInterface::GetSensorsPollInterface()
+{
+ if (mPollInterface) {
+ return mPollInterface.get();
+ }
+
+ mPollInterface = MakeUnique<GonkSensorsPollInterface>(mProtocol.get());
+
+ return mPollInterface.get();
+}
+
+GonkSensorsInterface::GonkSensorsInterface()
+ : mNotificationHandler(nullptr)
+{ }
+
+GonkSensorsInterface::~GonkSensorsInterface()
+{ }
+
+void
+GonkSensorsInterface::DispatchError(GonkSensorsResultHandler* aRes,
+ SensorsError aError)
+{
+ DaemonResultRunnable1<GonkSensorsResultHandler, void,
+ SensorsError, SensorsError>::Dispatch(
+ aRes, &GonkSensorsResultHandler::OnError,
+ ConstantInitOp1<SensorsError>(aError));
+}
+
+void
+GonkSensorsInterface::DispatchError(
+ GonkSensorsResultHandler* aRes, nsresult aRv)
+{
+ SensorsError error;
+
+ if (NS_FAILED(Convert(aRv, error))) {
+ error = SENSORS_ERROR_FAIL;
+ }
+ DispatchError(aRes, error);
+}
+
+// |DaemonSocketConsumer|, |ListenSocketConsumer|
+
+void
+GonkSensorsInterface::OnConnectSuccess(int aIndex)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(!mResultHandlerQ.IsEmpty());
+
+ switch (aIndex) {
+ case LISTEN_SOCKET: {
+ // Init, step 2: Start Sensors daemon
+ nsCString args("-a ");
+ args.Append(mListenSocketName);
+ mozilla::hal::StartSystemService("sensorsd", args.get());
+ }
+ break;
+ case DATA_SOCKET:
+ if (!mResultHandlerQ.IsEmpty()) {
+ // Init, step 3: Signal success
+ RefPtr<GonkSensorsResultHandler> res = mResultHandlerQ.ElementAt(0);
+ mResultHandlerQ.RemoveElementAt(0);
+ if (res) {
+ res->Connect();
+ }
+ }
+ break;
+ }
+}
+
+void
+GonkSensorsInterface::OnConnectError(int aIndex)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(!mResultHandlerQ.IsEmpty());
+
+ switch (aIndex) {
+ case DATA_SOCKET:
+ // Stop daemon and close listen socket
+ mozilla::hal::StopSystemService("sensorsd");
+ mListenSocket->Close();
+ // fall through
+ case LISTEN_SOCKET:
+ if (!mResultHandlerQ.IsEmpty()) {
+ // Signal error to caller
+ RefPtr<GonkSensorsResultHandler> res = mResultHandlerQ.ElementAt(0);
+ mResultHandlerQ.RemoveElementAt(0);
+ if (res) {
+ DispatchError(res, SENSORS_ERROR_FAIL);
+ }
+ }
+ break;
+ }
+}
+
+/*
+ * Disconnects can happend
+ *
+ * (a) during startup,
+ * (b) during regular service, or
+ * (c) during shutdown.
+ *
+ * For cases (a) and (c), |mResultHandlerQ| contains an element. For
+ * case (b) |mResultHandlerQ| will be empty. This distinguishes a crash in
+ * the daemon. The following procedure to recover from crashes consists of
+ * several steps for case (b).
+ *
+ * (1) Close listen socket.
+ * (2) Wait for all sockets to be disconnected and inform caller about
+ * the crash.
+ * (3) After all resources have been cleaned up, let the caller restart
+ * the daemon.
+ */
+void
+GonkSensorsInterface::OnDisconnect(int aIndex)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+
+ switch (aIndex) {
+ case DATA_SOCKET:
+ // Cleanup, step 2 (Recovery, step 1): Close listen socket
+ mListenSocket->Close();
+ break;
+ case LISTEN_SOCKET:
+ // Cleanup, step 3: Signal success to caller
+ if (!mResultHandlerQ.IsEmpty()) {
+ RefPtr<GonkSensorsResultHandler> res = mResultHandlerQ.ElementAt(0);
+ mResultHandlerQ.RemoveElementAt(0);
+ if (res) {
+ res->Disconnect();
+ }
+ }
+ break;
+ }
+
+ /* For recovery make sure all sockets disconnected, in order to avoid
+ * the remaining disconnects interfere with the restart procedure.
+ */
+ if (mNotificationHandler && mResultHandlerQ.IsEmpty()) {
+ if (mListenSocket->GetConnectionStatus() == SOCKET_DISCONNECTED &&
+ mDataSocket->GetConnectionStatus() == SOCKET_DISCONNECTED) {
+ // Recovery, step 2: Notify the caller to prepare the restart procedure.
+ mNotificationHandler->BackendErrorNotification(true);
+ mNotificationHandler = nullptr;
+ }
+ }
+}
+
+} // namespace hal
+} // namespace mozilla
diff --git a/hal/gonk/GonkSensorsInterface.h b/hal/gonk/GonkSensorsInterface.h
new file mode 100644
index 000000000..6e356dc36
--- /dev/null
+++ b/hal/gonk/GonkSensorsInterface.h
@@ -0,0 +1,191 @@
+/* -*- 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/. */
+
+/*
+ * The sensors interface gives you access to the low-level sensors code
+ * in a platform-independent manner. The interfaces in this file allow
+ * for starting an stopping the sensors driver. Specific functionality
+ * is implemented in sub-interfaces.
+ */
+
+#ifndef hal_gonk_GonkSensorsInterface_h
+#define hal_gonk_GonkSensorsInterface_h
+
+#include <mozilla/ipc/DaemonSocketConsumer.h>
+#include <mozilla/ipc/DaemonSocketMessageHandlers.h>
+#include <mozilla/ipc/ListenSocketConsumer.h>
+#include <mozilla/UniquePtr.h>
+#include "SensorsTypes.h"
+
+namespace mozilla {
+namespace ipc {
+
+class DaemonSocket;
+class ListenSocket;
+
+}
+}
+
+namespace mozilla {
+namespace hal {
+
+class GonkSensorsPollInterface;
+class GonkSensorsProtocol;
+class GonkSensorsRegistryInterface;
+
+/**
+ * This class is the result-handler interface for the Sensors
+ * interface. Methods always run on the main thread.
+ */
+class GonkSensorsResultHandler
+ : public mozilla::ipc::DaemonSocketResultHandler
+{
+public:
+
+ /**
+ * Called if a command failed.
+ *
+ * @param aError The error code.
+ */
+ virtual void OnError(SensorsError aError);
+
+ /**
+ * The callback method for |GonkSensorsInterface::Connect|.
+ */
+ virtual void Connect();
+
+ /**
+ * The callback method for |GonkSensorsInterface::Connect|.
+ */
+ virtual void Disconnect();
+
+protected:
+ virtual ~GonkSensorsResultHandler();
+};
+
+/**
+ * This is the notification-handler interface. Implement this classes
+ * methods to handle event and notifications from the sensors daemon.
+ * All methods run on the main thread.
+ */
+class GonkSensorsNotificationHandler
+{
+public:
+
+ /**
+ * This notification is called when the backend code fails
+ * unexpectedly. Save state in the high-level code and restart
+ * the driver.
+ *
+ * @param aCrash True is the sensors driver crashed.
+ */
+ virtual void BackendErrorNotification(bool aCrashed);
+
+protected:
+ virtual ~GonkSensorsNotificationHandler();
+};
+
+/**
+ * This class implements the public interface to the Sensors functionality
+ * and driver. Use |GonkSensorsInterface::GetInstance| to retrieve an instance.
+ * All methods run on the main thread.
+ */
+class GonkSensorsInterface final
+ : public mozilla::ipc::DaemonSocketConsumer
+ , public mozilla::ipc::ListenSocketConsumer
+{
+public:
+ /**
+ * Returns an instance of the Sensors backend. This code can return
+ * |nullptr| if no Sensors backend is available.
+ *
+ * @return An instance of |GonkSensorsInterface|.
+ */
+ static GonkSensorsInterface* GetInstance();
+
+ /**
+ * This method sets the notification handler for sensor notifications. Call
+ * this method immediately after retreiving an instance of the class, or you
+ * won't be able able to receive notifications. You may not free the handler
+ * class while the Sensors backend is connected.
+ *
+ * @param aNotificationHandler An instance of a notification handler.
+ */
+ void SetNotificationHandler(
+ GonkSensorsNotificationHandler* aNotificationHandler);
+
+ /**
+ * This method starts the Sensors backend and establishes ad connection
+ * with Gecko. This is a multi-step process and errors are signalled by
+ * |GonkSensorsNotificationHandler::BackendErrorNotification|. If you see
+ * this notification before the connection has been established, it's
+ * certainly best to assume the Sensors backend to be not evailable.
+ *
+ * @param aRes The result handler.
+ */
+ void Connect(GonkSensorsNotificationHandler* aNotificationHandler,
+ GonkSensorsResultHandler* aRes);
+
+ /**
+ * This method disconnects Gecko from the Sensors backend and frees
+ * the backend's resources. This will invalidate all interfaces and
+ * state. Don't use any sensors functionality without reconnecting
+ * first.
+ *
+ * @param aRes The result handler.
+ */
+ void Disconnect(GonkSensorsResultHandler* aRes);
+
+ /**
+ * Returns the Registry interface for the connected Sensors backend.
+ *
+ * @return An instance of the Sensors Registry interface.
+ */
+ GonkSensorsRegistryInterface* GetSensorsRegistryInterface();
+
+ /**
+ * Returns the Poll interface for the connected Sensors backend.
+ *
+ * @return An instance of the Sensors Poll interface.
+ */
+ GonkSensorsPollInterface* GetSensorsPollInterface();
+
+private:
+ enum Channel {
+ LISTEN_SOCKET,
+ DATA_SOCKET
+ };
+
+ GonkSensorsInterface();
+ ~GonkSensorsInterface();
+
+ void DispatchError(GonkSensorsResultHandler* aRes, SensorsError aError);
+ void DispatchError(GonkSensorsResultHandler* aRes, nsresult aRv);
+
+ // Methods for |DaemonSocketConsumer| and |ListenSocketConsumer|
+ //
+
+ void OnConnectSuccess(int aIndex) override;
+ void OnConnectError(int aIndex) override;
+ void OnDisconnect(int aIndex) override;
+
+ nsCString mListenSocketName;
+ RefPtr<mozilla::ipc::ListenSocket> mListenSocket;
+ RefPtr<mozilla::ipc::DaemonSocket> mDataSocket;
+ UniquePtr<GonkSensorsProtocol> mProtocol;
+
+ nsTArray<RefPtr<GonkSensorsResultHandler> > mResultHandlerQ;
+
+ GonkSensorsNotificationHandler* mNotificationHandler;
+
+ UniquePtr<GonkSensorsRegistryInterface> mRegistryInterface;
+ UniquePtr<GonkSensorsPollInterface> mPollInterface;
+};
+
+} // namespace hal
+} // namespace mozilla
+
+#endif // hal_gonk_GonkSensorsInterface_h
diff --git a/hal/gonk/GonkSensorsPollInterface.cpp b/hal/gonk/GonkSensorsPollInterface.cpp
new file mode 100644
index 000000000..d4edc2e7a
--- /dev/null
+++ b/hal/gonk/GonkSensorsPollInterface.cpp
@@ -0,0 +1,431 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 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 "GonkSensorsPollInterface.h"
+#include "HalLog.h"
+#include <mozilla/UniquePtr.h>
+
+namespace mozilla {
+namespace hal {
+
+using namespace mozilla::ipc;
+
+//
+// GonkSensorsPollResultHandler
+//
+
+void
+GonkSensorsPollResultHandler::OnError(SensorsError aError)
+{
+ HAL_ERR("Received error code %d", static_cast<int>(aError));
+}
+
+void
+GonkSensorsPollResultHandler::EnableSensor()
+{ }
+
+void
+GonkSensorsPollResultHandler::DisableSensor()
+{ }
+
+void
+GonkSensorsPollResultHandler::SetPeriod()
+{ }
+
+GonkSensorsPollResultHandler::~GonkSensorsPollResultHandler()
+{ }
+
+//
+// GonkSensorsPollNotificationHandler
+//
+
+void
+GonkSensorsPollNotificationHandler::ErrorNotification(SensorsError aError)
+{
+ HAL_ERR("Received error code %d", static_cast<int>(aError));
+}
+
+void
+GonkSensorsPollNotificationHandler::SensorDetectedNotification(
+ int32_t aId,
+ SensorsType aType,
+ float aRange,
+ float aResolution,
+ float aPower,
+ int32_t aMinPeriod,
+ int32_t aMaxPeriod,
+ SensorsTriggerMode aTriggerMode,
+ SensorsDeliveryMode aDeliveryMode)
+{ }
+
+void
+GonkSensorsPollNotificationHandler::SensorLostNotification(int32_t aId)
+{ }
+
+void
+GonkSensorsPollNotificationHandler::EventNotification(int32_t aId,
+ const SensorsEvent& aEvent)
+{ }
+
+GonkSensorsPollNotificationHandler::~GonkSensorsPollNotificationHandler()
+{ }
+
+//
+// GonkSensorsPollModule
+//
+
+GonkSensorsPollModule::GonkSensorsPollModule()
+ : mProtocolVersion(0)
+{ }
+
+GonkSensorsPollModule::~GonkSensorsPollModule()
+{ }
+
+nsresult
+GonkSensorsPollModule::SetProtocolVersion(unsigned long aProtocolVersion)
+{
+ if ((aProtocolVersion < MIN_PROTOCOL_VERSION) ||
+ (aProtocolVersion > MAX_PROTOCOL_VERSION)) {
+ HAL_ERR("Sensors Poll protocol version %lu not supported",
+ aProtocolVersion);
+ return NS_ERROR_ILLEGAL_VALUE;
+ }
+ mProtocolVersion = aProtocolVersion;
+ return NS_OK;
+}
+
+void
+GonkSensorsPollModule::HandleSvc(const DaemonSocketPDUHeader& aHeader,
+ DaemonSocketPDU& aPDU,
+ DaemonSocketResultHandler* aRes)
+{
+ static void (GonkSensorsPollModule::* const HandleOp[])(
+ const DaemonSocketPDUHeader&, DaemonSocketPDU&,
+ DaemonSocketResultHandler*) = {
+ [0] = &GonkSensorsPollModule::HandleRsp,
+ [1] = &GonkSensorsPollModule::HandleNtf
+ };
+
+ MOZ_ASSERT(!NS_IsMainThread()); // I/O thread
+
+ // Negate twice to map bit to 0/1
+ unsigned long isNtf = !!(aHeader.mOpcode & 0x80);
+
+ (this->*(HandleOp[isNtf]))(aHeader, aPDU, aRes);
+}
+
+// Commands
+//
+
+nsresult
+GonkSensorsPollModule::EnableSensorCmd(int32_t aId, GonkSensorsPollResultHandler* aRes)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+
+ UniquePtr<DaemonSocketPDU> pdu =
+ MakeUnique<DaemonSocketPDU>(SERVICE_ID, OPCODE_ENABLE_SENSOR, 0);
+
+ nsresult rv = PackPDU(aId, *pdu);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ rv = Send(pdu.get(), aRes);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ Unused << pdu.release();
+ return NS_OK;
+}
+
+nsresult
+GonkSensorsPollModule::DisableSensorCmd(int32_t aId, GonkSensorsPollResultHandler* aRes)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+
+ UniquePtr<DaemonSocketPDU> pdu =
+ MakeUnique<DaemonSocketPDU>(SERVICE_ID, OPCODE_DISABLE_SENSOR, 0);
+
+ nsresult rv = PackPDU(aId, *pdu);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ rv = Send(pdu.get(), aRes);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ Unused << pdu.release();
+ return NS_OK;
+}
+
+nsresult
+GonkSensorsPollModule::SetPeriodCmd(int32_t aId, uint64_t aPeriod,
+ GonkSensorsPollResultHandler* aRes)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+
+ UniquePtr<DaemonSocketPDU> pdu =
+ MakeUnique<DaemonSocketPDU>(SERVICE_ID, OPCODE_SET_PERIOD, 0);
+
+ nsresult rv = PackPDU(aId, *pdu);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ rv = PackPDU(aPeriod, *pdu);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ rv = Send(pdu.get(), aRes);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ Unused << pdu.release();
+ return NS_OK;
+}
+
+// Responses
+//
+
+void
+GonkSensorsPollModule::ErrorRsp(
+ const DaemonSocketPDUHeader& aHeader,
+ DaemonSocketPDU& aPDU, GonkSensorsPollResultHandler* aRes)
+{
+ ErrorRunnable::Dispatch(
+ aRes, &GonkSensorsPollResultHandler::OnError, UnpackPDUInitOp(aPDU));
+}
+
+void
+GonkSensorsPollModule::EnableSensorRsp(
+ const DaemonSocketPDUHeader& aHeader, DaemonSocketPDU& aPDU,
+ GonkSensorsPollResultHandler* aRes)
+{
+ ResultRunnable::Dispatch(
+ aRes, &GonkSensorsPollResultHandler::EnableSensor, UnpackPDUInitOp(aPDU));
+}
+
+void
+GonkSensorsPollModule::DisableSensorRsp(
+ const DaemonSocketPDUHeader& aHeader, DaemonSocketPDU& aPDU,
+ GonkSensorsPollResultHandler* aRes)
+{
+ ResultRunnable::Dispatch(
+ aRes, &GonkSensorsPollResultHandler::DisableSensor, UnpackPDUInitOp(aPDU));
+}
+
+void
+GonkSensorsPollModule::SetPeriodRsp(
+ const DaemonSocketPDUHeader& aHeader, DaemonSocketPDU& aPDU,
+ GonkSensorsPollResultHandler* aRes)
+{
+ ResultRunnable::Dispatch(
+ aRes, &GonkSensorsPollResultHandler::SetPeriod, UnpackPDUInitOp(aPDU));
+}
+
+void
+GonkSensorsPollModule::HandleRsp(
+ const DaemonSocketPDUHeader& aHeader, DaemonSocketPDU& aPDU,
+ DaemonSocketResultHandler* aRes)
+{
+ static void (GonkSensorsPollModule::* const sHandleRsp[])(
+ const DaemonSocketPDUHeader&, DaemonSocketPDU&,
+ GonkSensorsPollResultHandler*) = {
+ [OPCODE_ERROR] = &GonkSensorsPollModule::ErrorRsp,
+ [OPCODE_ENABLE_SENSOR] = &GonkSensorsPollModule::EnableSensorRsp,
+ [OPCODE_DISABLE_SENSOR] = &GonkSensorsPollModule::DisableSensorRsp,
+ [OPCODE_SET_PERIOD] = &GonkSensorsPollModule::SetPeriodRsp,
+ };
+
+ MOZ_ASSERT(!NS_IsMainThread()); // I/O thread
+
+ if (!(aHeader.mOpcode < MOZ_ARRAY_LENGTH(sHandleRsp)) ||
+ !sHandleRsp[aHeader.mOpcode]) {
+ HAL_ERR("Sensors poll response opcode %d unknown", aHeader.mOpcode);
+ return;
+ }
+
+ RefPtr<GonkSensorsPollResultHandler> res =
+ static_cast<GonkSensorsPollResultHandler*>(aRes);
+
+ if (!res) {
+ return; // Return early if no result handler has been set for response
+ }
+
+ (this->*(sHandleRsp[aHeader.mOpcode]))(aHeader, aPDU, res);
+}
+
+// Notifications
+//
+
+// Returns the current notification handler to a notification runnable
+class GonkSensorsPollModule::NotificationHandlerWrapper final
+{
+public:
+ typedef GonkSensorsPollNotificationHandler ObjectType;
+
+ static ObjectType* GetInstance()
+ {
+ MOZ_ASSERT(NS_IsMainThread());
+
+ return sNotificationHandler;
+ }
+
+ static GonkSensorsPollNotificationHandler* sNotificationHandler;
+};
+
+GonkSensorsPollNotificationHandler*
+ GonkSensorsPollModule::NotificationHandlerWrapper::sNotificationHandler;
+
+void
+GonkSensorsPollModule::ErrorNtf(
+ const DaemonSocketPDUHeader& aHeader, DaemonSocketPDU& aPDU)
+{
+ ErrorNotification::Dispatch(
+ &GonkSensorsPollNotificationHandler::ErrorNotification,
+ UnpackPDUInitOp(aPDU));
+}
+
+void
+GonkSensorsPollModule::SensorDetectedNtf(
+ const DaemonSocketPDUHeader& aHeader, DaemonSocketPDU& aPDU)
+{
+ SensorDetectedNotification::Dispatch(
+ &GonkSensorsPollNotificationHandler::SensorDetectedNotification,
+ UnpackPDUInitOp(aPDU));
+}
+
+void
+GonkSensorsPollModule::SensorLostNtf(
+ const DaemonSocketPDUHeader& aHeader, DaemonSocketPDU& aPDU)
+{
+ SensorLostNotification::Dispatch(
+ &GonkSensorsPollNotificationHandler::SensorLostNotification,
+ UnpackPDUInitOp(aPDU));
+}
+
+void
+GonkSensorsPollModule::EventNtf(
+ const DaemonSocketPDUHeader& aHeader, DaemonSocketPDU& aPDU)
+{
+ EventNotification::Dispatch(
+ &GonkSensorsPollNotificationHandler::EventNotification,
+ UnpackPDUInitOp(aPDU));
+}
+
+void
+GonkSensorsPollModule::HandleNtf(
+ const DaemonSocketPDUHeader& aHeader, DaemonSocketPDU& aPDU,
+ DaemonSocketResultHandler* aRes)
+{
+ static void (GonkSensorsPollModule::* const sHandleNtf[])(
+ const DaemonSocketPDUHeader&, DaemonSocketPDU&) = {
+ [0] = &GonkSensorsPollModule::ErrorNtf,
+ [1] = &GonkSensorsPollModule::SensorDetectedNtf,
+ [2] = &GonkSensorsPollModule::SensorLostNtf,
+ [3] = &GonkSensorsPollModule::EventNtf
+ };
+
+ MOZ_ASSERT(!NS_IsMainThread());
+
+ uint8_t index = aHeader.mOpcode - 0x80;
+
+ if (!(index < MOZ_ARRAY_LENGTH(sHandleNtf)) || !sHandleNtf[index]) {
+ HAL_ERR("Sensors poll notification opcode %d unknown", aHeader.mOpcode);
+ return;
+ }
+
+ (this->*(sHandleNtf[index]))(aHeader, aPDU);
+}
+
+//
+// GonkSensorsPollInterface
+//
+
+GonkSensorsPollInterface::GonkSensorsPollInterface(
+ GonkSensorsPollModule* aModule)
+ : mModule(aModule)
+{ }
+
+GonkSensorsPollInterface::~GonkSensorsPollInterface()
+{ }
+
+void
+GonkSensorsPollInterface::SetNotificationHandler(
+ GonkSensorsPollNotificationHandler* aNotificationHandler)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+
+ GonkSensorsPollModule::NotificationHandlerWrapper::sNotificationHandler =
+ aNotificationHandler;
+}
+
+nsresult
+GonkSensorsPollInterface::SetProtocolVersion(unsigned long aProtocolVersion)
+{
+ MOZ_ASSERT(mModule);
+
+ return mModule->SetProtocolVersion(aProtocolVersion);
+}
+
+void
+GonkSensorsPollInterface::EnableSensor(int32_t aId,
+ GonkSensorsPollResultHandler* aRes)
+{
+ MOZ_ASSERT(mModule);
+
+ nsresult rv = mModule->EnableSensorCmd(aId, aRes);
+ if (NS_FAILED(rv)) {
+ DispatchError(aRes, rv);
+ }
+}
+
+void
+GonkSensorsPollInterface::DisableSensor(int32_t aId,
+ GonkSensorsPollResultHandler* aRes)
+{
+ MOZ_ASSERT(mModule);
+
+ nsresult rv = mModule->DisableSensorCmd(aId, aRes);
+ if (NS_FAILED(rv)) {
+ DispatchError(aRes, rv);
+ }
+}
+
+void
+GonkSensorsPollInterface::SetPeriod(int32_t aId, uint64_t aPeriod,
+ GonkSensorsPollResultHandler* aRes)
+{
+ MOZ_ASSERT(mModule);
+
+ nsresult rv = mModule->SetPeriodCmd(aId, aPeriod, aRes);
+ if (NS_FAILED(rv)) {
+ DispatchError(aRes, rv);
+ }
+}
+
+void
+GonkSensorsPollInterface::DispatchError(
+ GonkSensorsPollResultHandler* aRes, SensorsError aError)
+{
+ DaemonResultRunnable1<GonkSensorsPollResultHandler, void,
+ SensorsError, SensorsError>::Dispatch(
+ aRes, &GonkSensorsPollResultHandler::OnError,
+ ConstantInitOp1<SensorsError>(aError));
+}
+
+void
+GonkSensorsPollInterface::DispatchError(
+ GonkSensorsPollResultHandler* aRes, nsresult aRv)
+{
+ SensorsError error;
+
+ if (NS_FAILED(Convert(aRv, error))) {
+ error = SENSORS_ERROR_FAIL;
+ }
+ DispatchError(aRes, error);
+}
+
+} // namespace hal
+} // namespace mozilla
diff --git a/hal/gonk/GonkSensorsPollInterface.h b/hal/gonk/GonkSensorsPollInterface.h
new file mode 100644
index 000000000..89381a9bd
--- /dev/null
+++ b/hal/gonk/GonkSensorsPollInterface.h
@@ -0,0 +1,340 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 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/. */
+
+/*
+ * The poll interface gives yo access to the Sensors daemon's Poll service,
+ * which handles sensors. The poll service will inform you when sensors are
+ * detected or removed from the system. You can activate (or deactivate)
+ * existing sensors and poll will deliver the sensors' events.
+ *
+ * All public methods and callback methods run on the main thread.
+ */
+
+#ifndef hal_gonk_GonkSensorsPollInterface_h
+#define hal_gonk_GonkSensorsPollInterface_h
+
+#include <mozilla/ipc/DaemonRunnables.h>
+#include <mozilla/ipc/DaemonSocketMessageHandlers.h>
+#include "SensorsTypes.h"
+
+namespace mozilla {
+namespace ipc {
+
+class DaemonSocketPDU;
+class DaemonSocketPDUHeader;
+
+}
+}
+
+namespace mozilla {
+namespace hal {
+
+class SensorsInterface;
+
+using mozilla::ipc::DaemonSocketPDU;
+using mozilla::ipc::DaemonSocketPDUHeader;
+using mozilla::ipc::DaemonSocketResultHandler;
+
+/**
+ * This class is the result-handler interface for the Sensors
+ * Poll interface. Methods always run on the main thread.
+ */
+class GonkSensorsPollResultHandler : public DaemonSocketResultHandler
+{
+public:
+
+ /**
+ * Called if a poll command failed.
+ *
+ * @param aError The error code.
+ */
+ virtual void OnError(SensorsError aError);
+
+ /**
+ * The callback method for |GonkSensorsPollInterface::EnableSensor|.
+ */
+ virtual void EnableSensor();
+
+ /**
+ * The callback method for |GonkSensorsPollInterface::DisableSensor|.
+ */
+ virtual void DisableSensor();
+
+ /**
+ * The callback method for |GonkSensorsPollInterface::SetPeriod|.
+ */
+ virtual void SetPeriod();
+
+protected:
+ virtual ~GonkSensorsPollResultHandler();
+};
+
+/**
+ * This is the notification-handler interface. Implement this classes
+ * methods to handle event and notifications from the sensors daemon.
+ */
+class GonkSensorsPollNotificationHandler
+{
+public:
+
+ /**
+ * The notification handler for errors. You'll receive this call if
+ * there's been a critical error in the daemon. Either try to handle
+ * the error, or restart the daemon.
+ *
+ * @param aError The error code.
+ */
+ virtual void ErrorNotification(SensorsError aError);
+
+ /**
+ * This methods gets call when a new sensor has been detected.
+ *
+ * @param aId The sensor's id.
+ * @param aType The sensor's type.
+ * @param aRange The sensor's maximum value.
+ * @param aResolution The minimum difference between two consecutive values.
+ * @param aPower The sensor's power consumption (in mA).
+ * @param aMinPeriod The minimum time between two events (in ns).
+ * @param aMaxPeriod The maximum time between two events (in ns).
+ * @param aTriggerMode The sensor's mode for triggering events.
+ * @param aDeliveryMode The sensor's urgency for event delivery.
+ */
+ virtual void SensorDetectedNotification(int32_t aId, SensorsType aType,
+ float aRange, float aResolution,
+ float aPower, int32_t aMinPeriod,
+ int32_t aMaxPeriod,
+ SensorsTriggerMode aTriggerMode,
+ SensorsDeliveryMode aDeliveryMode);
+
+ /**
+ * This methods gets call when an existing sensor has been removed.
+ *
+ * @param aId The sensor's id.
+ */
+ virtual void SensorLostNotification(int32_t aId);
+
+ /**
+ * This is the callback methods for sensor events. Only activated sensors
+ * generate events. All sensors are disabled by default. The actual data
+ * of the event depends on the sensor type.
+ *
+ * @param aId The sensor's id.
+ * @param aEvent The event's data.
+ */
+ virtual void EventNotification(int32_t aId, const SensorsEvent& aEvent);
+
+protected:
+ virtual ~GonkSensorsPollNotificationHandler();
+};
+
+/**
+ * This is the module class for the Sensors poll component. It handles PDU
+ * packing and unpacking. Methods are either executed on the main thread or
+ * the I/O thread.
+ *
+ * This is an internal class, use |GonkSensorsPollInterface| instead.
+ */
+class GonkSensorsPollModule
+{
+public:
+ class NotificationHandlerWrapper;
+
+ enum {
+ SERVICE_ID = 0x01
+ };
+
+ enum {
+ OPCODE_ERROR = 0x00,
+ OPCODE_ENABLE_SENSOR = 0x01,
+ OPCODE_DISABLE_SENSOR = 0x02,
+ OPCODE_SET_PERIOD = 0x03
+ };
+
+ enum {
+ MIN_PROTOCOL_VERSION = 1,
+ MAX_PROTOCOL_VERSION = 1
+ };
+
+ virtual nsresult Send(DaemonSocketPDU* aPDU,
+ DaemonSocketResultHandler* aRes) = 0;
+
+ nsresult SetProtocolVersion(unsigned long aProtocolVersion);
+
+ //
+ // Commands
+ //
+
+ nsresult EnableSensorCmd(int32_t aId,
+ GonkSensorsPollResultHandler* aRes);
+
+ nsresult DisableSensorCmd(int32_t aId,
+ GonkSensorsPollResultHandler* aRes);
+
+ nsresult SetPeriodCmd(int32_t aId, uint64_t aPeriod,
+ GonkSensorsPollResultHandler* aRes);
+
+protected:
+ GonkSensorsPollModule();
+ virtual ~GonkSensorsPollModule();
+
+ void HandleSvc(const DaemonSocketPDUHeader& aHeader,
+ DaemonSocketPDU& aPDU,
+ DaemonSocketResultHandler* aRes);
+
+private:
+
+ //
+ // Responses
+ //
+
+ typedef mozilla::ipc::DaemonResultRunnable0<
+ GonkSensorsPollResultHandler, void>
+ ResultRunnable;
+
+ typedef mozilla::ipc::DaemonResultRunnable1<
+ GonkSensorsPollResultHandler, void, SensorsError, SensorsError>
+ ErrorRunnable;
+
+ void ErrorRsp(const DaemonSocketPDUHeader& aHeader,
+ DaemonSocketPDU& aPDU,
+ GonkSensorsPollResultHandler* aRes);
+
+ void EnableSensorRsp(const DaemonSocketPDUHeader& aHeader,
+ DaemonSocketPDU& aPDU,
+ GonkSensorsPollResultHandler* aRes);
+
+ void DisableSensorRsp(const DaemonSocketPDUHeader& aHeader,
+ DaemonSocketPDU& aPDU,
+ GonkSensorsPollResultHandler* aRes);
+
+ void SetPeriodRsp(const DaemonSocketPDUHeader& aHeader,
+ DaemonSocketPDU& aPDU,
+ GonkSensorsPollResultHandler* aRes);
+
+ void HandleRsp(const DaemonSocketPDUHeader& aHeader,
+ DaemonSocketPDU& aPDU,
+ DaemonSocketResultHandler* aRes);
+
+ //
+ // Notifications
+ //
+
+ typedef mozilla::ipc::DaemonNotificationRunnable1<
+ NotificationHandlerWrapper, void, SensorsError>
+ ErrorNotification;
+
+ typedef mozilla::ipc::DaemonNotificationRunnable9<
+ NotificationHandlerWrapper, void, int32_t, SensorsType,
+ float, float, float, int32_t, int32_t, SensorsTriggerMode,
+ SensorsDeliveryMode>
+ SensorDetectedNotification;
+
+ typedef mozilla::ipc::DaemonNotificationRunnable1<
+ NotificationHandlerWrapper, void, int32_t>
+ SensorLostNotification;
+
+ typedef mozilla::ipc::DaemonNotificationRunnable2<
+ NotificationHandlerWrapper, void, int32_t, SensorsEvent, int32_t,
+ const SensorsEvent&>
+ EventNotification;
+
+ class SensorDetectedInitOp;
+ class SensorLostInitOp;
+ class EventInitOp;
+
+ void ErrorNtf(const DaemonSocketPDUHeader& aHeader,
+ DaemonSocketPDU& aPDU);
+
+ void SensorDetectedNtf(const DaemonSocketPDUHeader& aHeader,
+ DaemonSocketPDU& aPDU);
+
+ void SensorLostNtf(const DaemonSocketPDUHeader& aHeader,
+ DaemonSocketPDU& aPDU);
+
+ void EventNtf(const DaemonSocketPDUHeader& aHeader,
+ DaemonSocketPDU& aPDU);
+
+ void HandleNtf(const DaemonSocketPDUHeader& aHeader,
+ DaemonSocketPDU& aPDU,
+ DaemonSocketResultHandler* aRes);
+
+private:
+ unsigned long mProtocolVersion;
+};
+
+/**
+ * This class implements the public interface to the Sensors poll
+ * component. Use |SensorsInterface::GetPollInterface| to retrieve
+ * an instance. All methods run on the main thread.
+ */
+class GonkSensorsPollInterface final
+{
+public:
+ GonkSensorsPollInterface(GonkSensorsPollModule* aModule);
+ ~GonkSensorsPollInterface();
+
+ /**
+ * This method sets the notification handler for poll notifications. Call
+ * this method immediately after registering the module. Otherwise you won't
+ * be able able to receive poll notifications. You may not free the handler
+ * class while the poll component is regsitered.
+ *
+ * @param aNotificationHandler An instance of a poll notification handler.
+ */
+ void SetNotificationHandler(
+ GonkSensorsPollNotificationHandler* aNotificationHandler);
+
+ /**
+ * This method sets the protocol version. You should set it to the
+ * value that has been returned from the backend when registering the
+ * Poll service. You cannot send or receive messages before setting
+ * the protocol version.
+ *
+ * @param aProtocolVersion
+ * @return NS_OK for supported versions, or an XPCOM error code otherwise.
+ */
+ nsresult SetProtocolVersion(unsigned long aProtocolVersion);
+
+ /**
+ * Enables an existing sensor. The sensor id will have been delivered in
+ * a SensorDetectedNotification.
+ *
+ * @param aId The sensor's id.
+ * @param aRes The result handler.
+ */
+ void EnableSensor(int32_t aId, GonkSensorsPollResultHandler* aRes);
+
+ /**
+ * Disables an existing sensor. The sensor id will have been delivered in
+ * a SensorDetectedNotification.
+ *
+ * @param aId The sensor's id.
+ * @param aRes The result handler.
+ */
+ void DisableSensor(int32_t aId, GonkSensorsPollResultHandler* aRes);
+
+ /**
+ * Sets the period for a sensor. The sensor id will have been delivered in
+ * a SensorDetectedNotification. The value for the period should be between
+ * the sensor's minimum and maximum period.
+ *
+ * @param aId The sensor's id.
+ * @param aPeriod The sensor's new period.
+ * @param aRes The result handler.
+ */
+ void SetPeriod(int32_t aId, uint64_t aPeriod, GonkSensorsPollResultHandler* aRes);
+
+private:
+ void DispatchError(GonkSensorsPollResultHandler* aRes, SensorsError aError);
+ void DispatchError(GonkSensorsPollResultHandler* aRes, nsresult aRv);
+
+ GonkSensorsPollModule* mModule;
+};
+
+} // hal
+} // namespace mozilla
+
+#endif // hal_gonk_GonkSensorsPollInterface_h
diff --git a/hal/gonk/GonkSensorsRegistryInterface.cpp b/hal/gonk/GonkSensorsRegistryInterface.cpp
new file mode 100644
index 000000000..601dc7a2a
--- /dev/null
+++ b/hal/gonk/GonkSensorsRegistryInterface.cpp
@@ -0,0 +1,213 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 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 "GonkSensorsRegistryInterface.h"
+#include "GonkSensorsHelpers.h"
+#include "HalLog.h"
+#include <mozilla/UniquePtr.h>
+
+namespace mozilla {
+namespace hal {
+
+using namespace mozilla::ipc;
+
+//
+// GonkSensorsRegistryResultHandler
+//
+
+void
+GonkSensorsRegistryResultHandler::OnError(SensorsError aError)
+{
+ HAL_ERR("Received error code %d", static_cast<int>(aError));
+}
+
+void
+GonkSensorsRegistryResultHandler::RegisterModule(uint32_t aProtocolVersion)
+{ }
+
+void
+GonkSensorsRegistryResultHandler::UnregisterModule()
+{ }
+
+GonkSensorsRegistryResultHandler::~GonkSensorsRegistryResultHandler()
+{ }
+
+//
+// GonkSensorsRegistryModule
+//
+
+GonkSensorsRegistryModule::~GonkSensorsRegistryModule()
+{ }
+
+void
+GonkSensorsRegistryModule::HandleSvc(const DaemonSocketPDUHeader& aHeader,
+ DaemonSocketPDU& aPDU,
+ DaemonSocketResultHandler* aRes)
+{
+ static void (GonkSensorsRegistryModule::* const HandleRsp[])(
+ const DaemonSocketPDUHeader&,
+ DaemonSocketPDU&,
+ GonkSensorsRegistryResultHandler*) = {
+ [OPCODE_ERROR] = &GonkSensorsRegistryModule::ErrorRsp,
+ [OPCODE_REGISTER_MODULE] = &GonkSensorsRegistryModule::RegisterModuleRsp,
+ [OPCODE_UNREGISTER_MODULE] = &GonkSensorsRegistryModule::UnregisterModuleRsp
+ };
+
+ if ((aHeader.mOpcode >= MOZ_ARRAY_LENGTH(HandleRsp)) ||
+ !HandleRsp[aHeader.mOpcode]) {
+ HAL_ERR("Sensors registry response opcode %d unknown", aHeader.mOpcode);
+ return;
+ }
+
+ RefPtr<GonkSensorsRegistryResultHandler> res =
+ static_cast<GonkSensorsRegistryResultHandler*>(aRes);
+
+ if (!res) {
+ return; // Return early if no result handler has been set
+ }
+
+ (this->*(HandleRsp[aHeader.mOpcode]))(aHeader, aPDU, res);
+}
+
+// Commands
+//
+
+nsresult
+GonkSensorsRegistryModule::RegisterModuleCmd(
+ uint8_t aId, GonkSensorsRegistryResultHandler* aRes)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+
+ UniquePtr<DaemonSocketPDU> pdu =
+ MakeUnique<DaemonSocketPDU>(SERVICE_ID, OPCODE_REGISTER_MODULE, 0);
+
+ nsresult rv = PackPDU(aId, *pdu);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ rv = Send(pdu.get(), aRes);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ Unused << pdu.release();
+ return NS_OK;
+}
+
+nsresult
+GonkSensorsRegistryModule::UnregisterModuleCmd(
+ uint8_t aId, GonkSensorsRegistryResultHandler* aRes)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+
+ UniquePtr<DaemonSocketPDU> pdu =
+ MakeUnique<DaemonSocketPDU>(SERVICE_ID, OPCODE_UNREGISTER_MODULE, 0);
+
+ nsresult rv = PackPDU(aId, *pdu);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ rv = Send(pdu.get(), aRes);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ Unused << pdu.release();
+ return NS_OK;
+}
+
+// Responses
+//
+
+void
+GonkSensorsRegistryModule::ErrorRsp(
+ const DaemonSocketPDUHeader& aHeader,
+ DaemonSocketPDU& aPDU, GonkSensorsRegistryResultHandler* aRes)
+{
+ ErrorRunnable::Dispatch(
+ aRes, &GonkSensorsRegistryResultHandler::OnError, UnpackPDUInitOp(aPDU));
+}
+
+void
+GonkSensorsRegistryModule::RegisterModuleRsp(
+ const DaemonSocketPDUHeader& aHeader, DaemonSocketPDU& aPDU,
+ GonkSensorsRegistryResultHandler* aRes)
+{
+ Uint32ResultRunnable::Dispatch(
+ aRes,
+ &GonkSensorsRegistryResultHandler::RegisterModule,
+ UnpackPDUInitOp(aPDU));
+}
+
+void
+GonkSensorsRegistryModule::UnregisterModuleRsp(
+ const DaemonSocketPDUHeader& aHeader, DaemonSocketPDU& aPDU,
+ GonkSensorsRegistryResultHandler* aRes)
+{
+ ResultRunnable::Dispatch(
+ aRes,
+ &GonkSensorsRegistryResultHandler::UnregisterModule,
+ UnpackPDUInitOp(aPDU));
+}
+
+//
+// GonkSensorsRegistryInterface
+//
+
+GonkSensorsRegistryInterface::GonkSensorsRegistryInterface(
+ GonkSensorsRegistryModule* aModule)
+ : mModule(aModule)
+{ }
+
+GonkSensorsRegistryInterface::~GonkSensorsRegistryInterface()
+{ }
+
+void
+GonkSensorsRegistryInterface::RegisterModule(
+ uint8_t aId, GonkSensorsRegistryResultHandler* aRes)
+{
+ MOZ_ASSERT(mModule);
+
+ nsresult rv = mModule->RegisterModuleCmd(aId, aRes);
+ if (NS_FAILED(rv)) {
+ DispatchError(aRes, rv);
+ }
+}
+
+void
+GonkSensorsRegistryInterface::UnregisterModule(
+ uint8_t aId, GonkSensorsRegistryResultHandler* aRes)
+{
+ MOZ_ASSERT(mModule);
+
+ nsresult rv = mModule->UnregisterModuleCmd(aId, aRes);
+ if (NS_FAILED(rv)) {
+ DispatchError(aRes, rv);
+ }
+}
+
+void
+GonkSensorsRegistryInterface::DispatchError(
+ GonkSensorsRegistryResultHandler* aRes, SensorsError aError)
+{
+ DaemonResultRunnable1<GonkSensorsRegistryResultHandler, void,
+ SensorsError, SensorsError>::Dispatch(
+ aRes, &GonkSensorsRegistryResultHandler::OnError,
+ ConstantInitOp1<SensorsError>(aError));
+}
+
+void
+GonkSensorsRegistryInterface::DispatchError(
+ GonkSensorsRegistryResultHandler* aRes, nsresult aRv)
+{
+ SensorsError error;
+
+ if (NS_FAILED(Convert(aRv, error))) {
+ error = SENSORS_ERROR_FAIL;
+ }
+ DispatchError(aRes, error);
+}
+
+} // namespace hal
+} // namespace mozilla
diff --git a/hal/gonk/GonkSensorsRegistryInterface.h b/hal/gonk/GonkSensorsRegistryInterface.h
new file mode 100644
index 000000000..a9d98d653
--- /dev/null
+++ b/hal/gonk/GonkSensorsRegistryInterface.h
@@ -0,0 +1,182 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 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/. */
+
+/*
+ * The registry interface gives yo access to the Sensors daemon's Registry
+ * service. The purpose of the service is to register and setup all other
+ * services, and make them available.
+ *
+ * All public methods and callback methods run on the main thread.
+ */
+
+#ifndef hal_gonk_GonkSensorsRegistryInterface_h
+#define hal_gonk_GonkSensorsRegistryInterface_h
+
+#include <mozilla/ipc/DaemonRunnables.h>
+#include <mozilla/ipc/DaemonSocketMessageHandlers.h>
+#include "SensorsTypes.h"
+
+namespace mozilla {
+namespace ipc {
+
+class DaemonSocketPDU;
+class DaemonSocketPDUHeader;
+
+}
+}
+
+namespace mozilla {
+namespace hal {
+
+class SensorsInterface;
+
+using mozilla::ipc::DaemonSocketPDU;
+using mozilla::ipc::DaemonSocketPDUHeader;
+using mozilla::ipc::DaemonSocketResultHandler;
+
+/**
+ * This class is the result-handler interface for the Sensors
+ * Registry interface. Methods always run on the main thread.
+ */
+class GonkSensorsRegistryResultHandler : public DaemonSocketResultHandler
+{
+public:
+
+ /**
+ * Called if a registry command failed.
+ *
+ * @param aError The error code.
+ */
+ virtual void OnError(SensorsError aError);
+
+ /**
+ * The callback method for |GonkSensorsRegistryInterface::RegisterModule|.
+ *
+ * @param aProtocolVersion The daemon's protocol version. Make sure it's
+ * compatible with Gecko's implementation.
+ */
+ virtual void RegisterModule(uint32_t aProtocolVersion);
+
+ /**
+ * The callback method for |SensorsRegsitryInterface::UnregisterModule|.
+ */
+ virtual void UnregisterModule();
+
+protected:
+ virtual ~GonkSensorsRegistryResultHandler();
+};
+
+/**
+ * This is the module class for the Sensors registry component. It handles
+ * PDU packing and unpacking. Methods are either executed on the main thread
+ * or the I/O thread.
+ *
+ * This is an internal class, use |GonkSensorsRegistryInterface| instead.
+ */
+class GonkSensorsRegistryModule
+{
+public:
+ enum {
+ SERVICE_ID = 0x00
+ };
+
+ enum {
+ OPCODE_ERROR = 0x00,
+ OPCODE_REGISTER_MODULE = 0x01,
+ OPCODE_UNREGISTER_MODULE = 0x02
+ };
+
+ virtual nsresult Send(DaemonSocketPDU* aPDU,
+ DaemonSocketResultHandler* aRes) = 0;
+
+ //
+ // Commands
+ //
+
+ nsresult RegisterModuleCmd(uint8_t aId,
+ GonkSensorsRegistryResultHandler* aRes);
+
+ nsresult UnregisterModuleCmd(uint8_t aId,
+ GonkSensorsRegistryResultHandler* aRes);
+
+protected:
+ virtual ~GonkSensorsRegistryModule();
+
+ void HandleSvc(const DaemonSocketPDUHeader& aHeader,
+ DaemonSocketPDU& aPDU, DaemonSocketResultHandler* aRes);
+
+ //
+ // Responses
+ //
+
+ typedef mozilla::ipc::DaemonResultRunnable0<
+ GonkSensorsRegistryResultHandler, void>
+ ResultRunnable;
+
+ typedef mozilla::ipc::DaemonResultRunnable1<
+ GonkSensorsRegistryResultHandler, void, uint32_t, uint32_t>
+ Uint32ResultRunnable;
+
+ typedef mozilla::ipc::DaemonResultRunnable1<
+ GonkSensorsRegistryResultHandler, void, SensorsError, SensorsError>
+ ErrorRunnable;
+
+ void ErrorRsp(const DaemonSocketPDUHeader& aHeader,
+ DaemonSocketPDU& aPDU,
+ GonkSensorsRegistryResultHandler* aRes);
+
+ void RegisterModuleRsp(const DaemonSocketPDUHeader& aHeader,
+ DaemonSocketPDU& aPDU,
+ GonkSensorsRegistryResultHandler* aRes);
+
+ void UnregisterModuleRsp(const DaemonSocketPDUHeader& aHeader,
+ DaemonSocketPDU& aPDU,
+ GonkSensorsRegistryResultHandler* aRes);
+};
+
+/**
+ * This class implements the public interface to the Sensors Registry
+ * component. Use |SensorsInterface::GetRegistryInterface| to retrieve
+ * an instance. All methods run on the main thread.
+ */
+class GonkSensorsRegistryInterface final
+{
+public:
+ GonkSensorsRegistryInterface(GonkSensorsRegistryModule* aModule);
+ ~GonkSensorsRegistryInterface();
+
+ /**
+ * Sends a RegisterModule command to the Sensors daemon. When the
+ * result handler's |RegisterModule| method gets called, the service
+ * has been registered successfully and can be used.
+ *
+ * @param aId The id of the service that is to be registered.
+ * @param aRes The result handler.
+ */
+ void RegisterModule(uint8_t aId, GonkSensorsRegistryResultHandler* aRes);
+
+ /**
+ * Sends an UnregisterModule command to the Sensors daemon. The service
+ * should not be used afterwards until it has been registered again.
+ *
+ * @param aId The id of the service that is to be unregistered.
+ * @param aRes The result handler.
+ */
+ void UnregisterModule(uint8_t aId, GonkSensorsRegistryResultHandler* aRes);
+
+private:
+ void DispatchError(GonkSensorsRegistryResultHandler* aRes,
+ SensorsError aError);
+ void DispatchError(GonkSensorsRegistryResultHandler* aRes,
+ nsresult aRv);
+
+ GonkSensorsRegistryModule* mModule;
+};
+
+} // namespace hal
+} // namespace mozilla
+
+#endif // hal_gonk_GonkSensorsRegistryInterface_h
diff --git a/hal/gonk/GonkSwitch.cpp b/hal/gonk/GonkSwitch.cpp
new file mode 100644
index 000000000..b2c31c973
--- /dev/null
+++ b/hal/gonk/GonkSwitch.cpp
@@ -0,0 +1,479 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* Copyright 2012 Mozilla Foundation and Mozilla contributors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <fcntl.h>
+#include <sysutils/NetlinkEvent.h>
+
+#include "base/message_loop.h"
+#include "base/task.h"
+
+#include "Hal.h"
+#include "HalLog.h"
+#include "mozilla/FileUtils.h"
+#include "mozilla/RefPtr.h"
+#include "mozilla/Monitor.h"
+#include "nsPrintfCString.h"
+#include "nsXULAppAPI.h"
+#include "nsThreadUtils.h"
+#include "UeventPoller.h"
+
+using namespace mozilla::hal;
+
+#define SWITCH_HEADSET_DEVPATH "/devices/virtual/switch/h2w"
+#define SWITCH_USB_DEVPATH_GB "/devices/virtual/switch/usb_configuration"
+#define SWITCH_USB_DEVPATH_ICS "/devices/virtual/android_usb/android0"
+
+namespace mozilla {
+namespace hal_impl {
+/**
+ * The uevent for a usb on GB insertion looks like:
+ *
+ * change@/devices/virtual/switch/usb_configuration
+ * ACTION=change
+ * DEVPATH=/devices/virtual/switch/usb_configuration
+ * SUBSYSTEM=switch
+ * SWITCH_NAME=usb_configuration
+ * SWITCH_STATE=0
+ * SEQNUM=5038
+ */
+class SwitchHandler
+{
+public:
+ NS_INLINE_DECL_REFCOUNTING(SwitchHandler)
+
+ SwitchHandler(const char* aDevPath, SwitchDevice aDevice)
+ : mDevPath(aDevPath),
+ mState(SWITCH_STATE_UNKNOWN),
+ mDevice(aDevice)
+ {
+ GetInitialState();
+ }
+
+ bool CheckEvent(NetlinkEvent* aEvent)
+ {
+ if (strcmp(GetSubsystem(), aEvent->getSubsystem()) ||
+ strcmp(mDevPath, aEvent->findParam("DEVPATH"))) {
+ return false;
+ }
+
+ mState = ConvertState(GetStateString(aEvent));
+ return mState != SWITCH_STATE_UNKNOWN;
+ }
+
+ SwitchState GetState()
+ {
+ return mState;
+ }
+
+ SwitchDevice GetType()
+ {
+ return mDevice;
+ }
+protected:
+ virtual ~SwitchHandler()
+ {
+ }
+
+ virtual const char* GetSubsystem()
+ {
+ return "switch";
+ }
+
+ virtual const char* GetStateString(NetlinkEvent* aEvent)
+ {
+ return aEvent->findParam("SWITCH_STATE");
+ }
+
+ void GetInitialState()
+ {
+ nsPrintfCString statePath("/sys%s/state", mDevPath);
+ int fd = open(statePath.get(), O_RDONLY);
+ if (fd <= 0) {
+ return;
+ }
+
+ ScopedClose autoClose(fd);
+ char state[16];
+ ssize_t bytesRead = read(fd, state, sizeof(state));
+ if (bytesRead < 0) {
+ HAL_ERR("Read data from %s fails", statePath.get());
+ return;
+ }
+
+ if (state[bytesRead - 1] == '\n') {
+ bytesRead--;
+ }
+
+ state[bytesRead] = '\0';
+ mState = ConvertState(state);
+ }
+
+ virtual SwitchState ConvertState(const char* aState)
+ {
+ MOZ_ASSERT(aState);
+ return aState[0] == '0' ? SWITCH_STATE_OFF : SWITCH_STATE_ON;
+ }
+
+ const char* mDevPath;
+ SwitchState mState;
+ SwitchDevice mDevice;
+};
+
+/**
+ * The uevent delivered for the USB configuration under ICS looks like,
+ *
+ * change@/devices/virtual/android_usb/android0
+ * ACTION=change
+ * DEVPATH=/devices/virtual/android_usb/android0
+ * SUBSYSTEM=android_usb
+ * USB_STATE=CONFIGURED
+ * SEQNUM=1802
+ */
+class SwitchHandlerUsbIcs: public SwitchHandler
+{
+public:
+ SwitchHandlerUsbIcs(const char* aDevPath) : SwitchHandler(aDevPath, SWITCH_USB)
+ {
+ SwitchHandler::GetInitialState();
+ }
+
+ virtual ~SwitchHandlerUsbIcs() { }
+
+protected:
+ virtual const char* GetSubsystem()
+ {
+ return "android_usb";
+ }
+
+ virtual const char* GetStateString(NetlinkEvent* aEvent)
+ {
+ return aEvent->findParam("USB_STATE");
+ }
+
+ SwitchState ConvertState(const char* aState)
+ {
+ MOZ_ASSERT(aState);
+ return strcmp(aState, "CONFIGURED") == 0 ? SWITCH_STATE_ON : SWITCH_STATE_OFF;
+ }
+};
+
+/**
+ * The uevent delivered for the headset under ICS looks like,
+ *
+ * change@/devices/virtual/switch/h2w
+ * ACTION=change
+ * DEVPATH=/devices/virtual/switch/h2w
+ * SUBSYSTEM=switch
+ * SWITCH_NAME=h2w
+ * SWITCH_STATE=2 // Headset with no mic
+ * SEQNUM=2581
+ * On Otoro, SWITCH_NAME could be Headset/No Device when plug/unplug.
+ * change@/devices/virtual/switch/h2w
+ * ACTION=change
+ * DEVPATH=/devices/virtual/switch/h2w
+ * SUBSYSTEM=switch
+ * SWITCH_NAME=Headset
+ * SWITCH_STATE=1 // Headset with mic
+ * SEQNUM=1602
+ */
+class SwitchHandlerHeadphone: public SwitchHandler
+{
+public:
+ SwitchHandlerHeadphone(const char* aDevPath) :
+ SwitchHandler(aDevPath, SWITCH_HEADPHONES)
+ {
+ SwitchHandler::GetInitialState();
+ }
+
+ virtual ~SwitchHandlerHeadphone() { }
+
+protected:
+ SwitchState ConvertState(const char* aState)
+ {
+ MOZ_ASSERT(aState);
+
+ return aState[0] == '0' ? SWITCH_STATE_OFF :
+ (aState[0] == '1' ? SWITCH_STATE_HEADSET : SWITCH_STATE_HEADPHONE);
+ }
+};
+
+
+typedef nsTArray<RefPtr<SwitchHandler> > SwitchHandlerArray;
+
+class SwitchEventRunnable : public Runnable
+{
+public:
+ SwitchEventRunnable(SwitchEvent& aEvent) : mEvent(aEvent)
+ {
+ }
+
+ NS_IMETHOD Run() override
+ {
+ NotifySwitchChange(mEvent);
+ return NS_OK;
+ }
+private:
+ SwitchEvent mEvent;
+};
+
+class SwitchEventObserver final : public IUeventObserver
+{
+ ~SwitchEventObserver()
+ {
+ mHandler.Clear();
+ }
+
+public:
+ NS_INLINE_DECL_REFCOUNTING(SwitchEventObserver)
+ SwitchEventObserver()
+ : mEnableCount(0),
+ mHeadphonesFromInputDev(false)
+ {
+ Init();
+ }
+
+ int GetEnableCount()
+ {
+ return mEnableCount;
+ }
+
+ void EnableSwitch(SwitchDevice aDevice)
+ {
+ mEventInfo[aDevice].mEnabled = true;
+ mEnableCount++;
+ }
+
+ void DisableSwitch(SwitchDevice aDevice)
+ {
+ mEventInfo[aDevice].mEnabled = false;
+ mEnableCount--;
+ }
+
+ void Notify(const NetlinkEvent& aEvent)
+ {
+ SwitchState currState;
+
+ SwitchDevice device = GetEventInfo(aEvent, currState);
+ if (device == SWITCH_DEVICE_UNKNOWN) {
+ return;
+ }
+
+ EventInfo& info = mEventInfo[device];
+ if (currState == info.mEvent.status()) {
+ return;
+ }
+
+ info.mEvent.status() = currState;
+
+ if (info.mEnabled) {
+ NS_DispatchToMainThread(new SwitchEventRunnable(info.mEvent));
+ }
+ }
+
+ void Notify(SwitchDevice aDevice, SwitchState aState)
+ {
+ EventInfo& info = mEventInfo[aDevice];
+ if (aState == info.mEvent.status()) {
+ return;
+ }
+
+ info.mEvent.status() = aState;
+
+ if (info.mEnabled) {
+ NS_DispatchToMainThread(new SwitchEventRunnable(info.mEvent));
+ }
+ }
+
+ SwitchState GetCurrentInformation(SwitchDevice aDevice)
+ {
+ return mEventInfo[aDevice].mEvent.status();
+ }
+
+ void NotifyAnEvent(SwitchDevice aDevice)
+ {
+ EventInfo& info = mEventInfo[aDevice];
+ if (info.mEvent.status() != SWITCH_STATE_UNKNOWN) {
+ NS_DispatchToMainThread(new SwitchEventRunnable(info.mEvent));
+ }
+ }
+
+ bool GetHeadphonesFromInputDev()
+ {
+ return mHeadphonesFromInputDev;
+ }
+
+private:
+ class EventInfo
+ {
+ public:
+ EventInfo() : mEnabled(false)
+ {
+ mEvent.status() = SWITCH_STATE_UNKNOWN;
+ mEvent.device() = SWITCH_DEVICE_UNKNOWN;
+ }
+ SwitchEvent mEvent;
+ bool mEnabled;
+ };
+
+ EventInfo mEventInfo[NUM_SWITCH_DEVICE];
+ size_t mEnableCount;
+ SwitchHandlerArray mHandler;
+ bool mHeadphonesFromInputDev;
+
+ // This function might also get called on the main thread
+ // (from IsHeadphoneEventFromInputDev)
+ void Init()
+ {
+ RefPtr<SwitchHandlerHeadphone> switchHeadPhone =
+ new SwitchHandlerHeadphone(SWITCH_HEADSET_DEVPATH);
+
+ // If the initial state is unknown, it means the headphone event is from input dev
+ mHeadphonesFromInputDev = switchHeadPhone->GetState() == SWITCH_STATE_UNKNOWN ? true : false;
+
+ if (!mHeadphonesFromInputDev) {
+ mHandler.AppendElement(switchHeadPhone);
+ } else {
+ // If headphone status will be notified from input dev then initialize
+ // status to "off" and wait for event notification.
+ mEventInfo[SWITCH_HEADPHONES].mEvent.device() = SWITCH_HEADPHONES;
+ mEventInfo[SWITCH_HEADPHONES].mEvent.status() = SWITCH_STATE_OFF;
+ }
+ mHandler.AppendElement(new SwitchHandler(SWITCH_USB_DEVPATH_GB, SWITCH_USB));
+ mHandler.AppendElement(new SwitchHandlerUsbIcs(SWITCH_USB_DEVPATH_ICS));
+
+ SwitchHandlerArray::index_type handlerIndex;
+ SwitchHandlerArray::size_type numHandlers = mHandler.Length();
+
+ for (handlerIndex = 0; handlerIndex < numHandlers; handlerIndex++) {
+ SwitchState state = mHandler[handlerIndex]->GetState();
+ if (state == SWITCH_STATE_UNKNOWN) {
+ continue;
+ }
+
+ SwitchDevice device = mHandler[handlerIndex]->GetType();
+ mEventInfo[device].mEvent.device() = device;
+ mEventInfo[device].mEvent.status() = state;
+ }
+ }
+
+ SwitchDevice GetEventInfo(const NetlinkEvent& aEvent, SwitchState& aState)
+ {
+ //working around the android code not being const-correct
+ NetlinkEvent *e = const_cast<NetlinkEvent*>(&aEvent);
+
+ for (size_t i = 0; i < mHandler.Length(); i++) {
+ if (mHandler[i]->CheckEvent(e)) {
+ aState = mHandler[i]->GetState();
+ return mHandler[i]->GetType();
+ }
+ }
+ return SWITCH_DEVICE_UNKNOWN;
+ }
+};
+
+static RefPtr<SwitchEventObserver> sSwitchObserver;
+
+static void
+InitializeResourceIfNeed()
+{
+ if (!sSwitchObserver) {
+ sSwitchObserver = new SwitchEventObserver();
+ RegisterUeventListener(sSwitchObserver);
+ }
+}
+
+static void
+ReleaseResourceIfNeed()
+{
+ if (sSwitchObserver->GetEnableCount() == 0) {
+ UnregisterUeventListener(sSwitchObserver);
+ sSwitchObserver = nullptr;
+ }
+}
+
+static void
+EnableSwitchNotificationsIOThread(SwitchDevice aDevice, Monitor *aMonitor)
+{
+ InitializeResourceIfNeed();
+ sSwitchObserver->EnableSwitch(aDevice);
+ {
+ MonitorAutoLock lock(*aMonitor);
+ lock.Notify();
+ }
+
+ // Notify the latest state if IO thread has the information.
+ if (sSwitchObserver->GetEnableCount() > 1) {
+ sSwitchObserver->NotifyAnEvent(aDevice);
+ }
+}
+
+void
+EnableSwitchNotifications(SwitchDevice aDevice)
+{
+ Monitor monitor("EnableSwitch.monitor");
+ {
+ MonitorAutoLock lock(monitor);
+ XRE_GetIOMessageLoop()->PostTask(
+ NewRunnableFunction(EnableSwitchNotificationsIOThread, aDevice, &monitor));
+ lock.Wait();
+ }
+}
+
+static void
+DisableSwitchNotificationsIOThread(SwitchDevice aDevice)
+{
+ MOZ_ASSERT(sSwitchObserver->GetEnableCount());
+ sSwitchObserver->DisableSwitch(aDevice);
+ ReleaseResourceIfNeed();
+}
+
+void
+DisableSwitchNotifications(SwitchDevice aDevice)
+{
+ XRE_GetIOMessageLoop()->PostTask(
+ NewRunnableFunction(DisableSwitchNotificationsIOThread, aDevice));
+}
+
+SwitchState
+GetCurrentSwitchState(SwitchDevice aDevice)
+{
+ MOZ_ASSERT(sSwitchObserver && sSwitchObserver->GetEnableCount());
+ return sSwitchObserver->GetCurrentInformation(aDevice);
+}
+
+static void
+NotifySwitchStateIOThread(SwitchDevice aDevice, SwitchState aState)
+{
+ InitializeResourceIfNeed();
+ sSwitchObserver->Notify(aDevice, aState);
+}
+
+void NotifySwitchStateFromInputDevice(SwitchDevice aDevice, SwitchState aState)
+{
+ XRE_GetIOMessageLoop()->PostTask(
+ NewRunnableFunction(NotifySwitchStateIOThread, aDevice, aState));
+}
+
+bool IsHeadphoneEventFromInputDev()
+{
+ // Instead of calling InitializeResourceIfNeed, create new SwitchEventObserver
+ // to prevent calling RegisterUeventListener in main thread.
+ RefPtr<SwitchEventObserver> switchObserver = new SwitchEventObserver();
+ return switchObserver->GetHeadphonesFromInputDev();
+}
+
+} // hal_impl
+} //mozilla
diff --git a/hal/gonk/SensorsTypes.h b/hal/gonk/SensorsTypes.h
new file mode 100644
index 000000000..35c852f5a
--- /dev/null
+++ b/hal/gonk/SensorsTypes.h
@@ -0,0 +1,140 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 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 hal_gonk_SensorsTypes_h
+#define hal_gonk_SensorsTypes_h
+
+namespace mozilla {
+namespace hal {
+
+enum SensorsDeliveryMode {
+ SENSORS_DELIVERY_MODE_BEST_EFFORT,
+ SENSORS_DELIVERY_MODE_IMMEDIATE
+};
+
+enum SensorsError {
+ SENSORS_ERROR_NONE,
+ SENSORS_ERROR_FAIL,
+ SENSORS_ERROR_NOT_READY,
+ SENSORS_ERROR_NOMEM,
+ SENSORS_ERROR_BUSY,
+ SENSORS_ERROR_DONE,
+ SENSORS_ERROR_UNSUPPORTED,
+ SENSORS_ERROR_PARM_INVALID
+};
+
+enum SensorsStatus {
+ SENSORS_STATUS_NO_CONTACT,
+ SENSORS_STATUS_UNRELIABLE,
+ SENSORS_STATUS_ACCURACY_LOW,
+ SENSORS_STATUS_ACCURACY_MEDIUM,
+ SENSORS_STATUS_ACCURACY_HIGH
+};
+
+enum SensorsTriggerMode {
+ SENSORS_TRIGGER_MODE_CONTINUOUS,
+ SENSORS_TRIGGER_MODE_ON_CHANGE,
+ SENSORS_TRIGGER_MODE_ONE_SHOT,
+ SENSORS_TRIGGER_MODE_SPECIAL
+};
+
+enum SensorsType {
+ SENSORS_TYPE_ACCELEROMETER,
+ SENSORS_TYPE_GEOMAGNETIC_FIELD,
+ SENSORS_TYPE_ORIENTATION,
+ SENSORS_TYPE_GYROSCOPE,
+ SENSORS_TYPE_LIGHT,
+ SENSORS_TYPE_PRESSURE,
+ SENSORS_TYPE_TEMPERATURE,
+ SENSORS_TYPE_PROXIMITY,
+ SENSORS_TYPE_GRAVITY,
+ SENSORS_TYPE_LINEAR_ACCELERATION,
+ SENSORS_TYPE_ROTATION_VECTOR,
+ SENSORS_TYPE_RELATIVE_HUMIDITY,
+ SENSORS_TYPE_AMBIENT_TEMPERATURE,
+ SENSORS_TYPE_MAGNETIC_FIELD_UNCALIBRATED,
+ SENSORS_TYPE_GAME_ROTATION_VECTOR,
+ SENSORS_TYPE_GYROSCOPE_UNCALIBRATED,
+ SENSORS_TYPE_SIGNIFICANT_MOTION,
+ SENSORS_TYPE_STEP_DETECTED,
+ SENSORS_TYPE_STEP_COUNTER,
+ SENSORS_TYPE_GEOMAGNETIC_ROTATION_VECTOR,
+ SENSORS_TYPE_HEART_RATE,
+ SENSORS_TYPE_TILT_DETECTOR,
+ SENSORS_TYPE_WAKE_GESTURE,
+ SENSORS_TYPE_GLANCE_GESTURE,
+ SENSORS_TYPE_PICK_UP_GESTURE,
+ SENSORS_TYPE_WRIST_TILT_GESTURE,
+ SENSORS_NUM_TYPES
+};
+
+struct SensorsEvent {
+ SensorsType mType;
+ SensorsStatus mStatus;
+ SensorsDeliveryMode mDeliveryMode;
+ int64_t mTimestamp;
+ union {
+ float mFloat[6];
+ uint64_t mUint[1];
+ } mData;
+};
+
+/**
+ * |SensorsSensor| represents a device sensor; either single or composite.
+ */
+struct SensorsSensor {
+ SensorsSensor(int32_t aId, SensorsType aType,
+ float aRange, float aResolution,
+ float aPower, int32_t aMinPeriod,
+ int32_t aMaxPeriod,
+ SensorsTriggerMode aTriggerMode,
+ SensorsDeliveryMode aDeliveryMode)
+ : mId(aId)
+ , mType(aType)
+ , mRange(aRange)
+ , mResolution(aResolution)
+ , mPower(aPower)
+ , mMinPeriod(aMinPeriod)
+ , mMaxPeriod(aMaxPeriod)
+ , mTriggerMode(aTriggerMode)
+ , mDeliveryMode(aDeliveryMode)
+ { }
+
+ int32_t mId;
+ SensorsType mType;
+ float mRange;
+ float mResolution;
+ float mPower;
+ int32_t mMinPeriod;
+ int32_t mMaxPeriod;
+ SensorsTriggerMode mTriggerMode;
+ SensorsDeliveryMode mDeliveryMode;
+};
+
+/**
+ * |SensorClass| represents the status of a specific sensor type.
+ */
+struct SensorsSensorClass {
+ SensorsSensorClass()
+ : mActivated(0)
+ , mMinValue(0)
+ , mMaxValue(0)
+ { }
+
+ void UpdateFromSensor(const SensorsSensor& aSensor)
+ {
+ mMaxValue = std::max(aSensor.mRange, mMaxValue);
+ }
+
+ uint32_t mActivated;
+ float mMinValue;
+ float mMaxValue;
+};
+
+} // namespace hal
+} // namespace mozilla
+
+#endif // hal_gonk_SensorsTypes_h
diff --git a/hal/gonk/SystemService.cpp b/hal/gonk/SystemService.cpp
new file mode 100644
index 000000000..2b98f5fdd
--- /dev/null
+++ b/hal/gonk/SystemService.cpp
@@ -0,0 +1,131 @@
+/* -*- 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 <cutils/properties.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "HalLog.h"
+#include "nsITimer.h"
+#include "mozilla/Unused.h"
+
+namespace mozilla {
+namespace hal_impl {
+
+static const int sRetryInterval = 100; // ms
+
+bool
+SystemServiceIsRunning(const char* aSvcName)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+
+ char key[PROPERTY_KEY_MAX];
+ auto res = snprintf(key, sizeof(key), "init.svc.%s", aSvcName);
+
+ if (res < 0) {
+ HAL_ERR("snprintf: %s", strerror(errno));
+ return false;
+ } else if (static_cast<size_t>(res) >= sizeof(key)) {
+ HAL_ERR("snprintf: trunctated service name %s", aSvcName);
+ return false;
+ }
+
+ char value[PROPERTY_VALUE_MAX];
+ Unused << NS_WARN_IF(property_get(key, value, "") < 0);
+
+ return !strcmp(value, "running");
+}
+
+class StartSystemServiceTimerCallback final : public nsITimerCallback
+{
+ NS_DECL_THREADSAFE_ISUPPORTS;
+
+public:
+ StartSystemServiceTimerCallback(const char* aSvcName, const char* aArgs)
+ : mSvcName(aSvcName)
+ , mArgs(aArgs)
+ {
+ MOZ_COUNT_CTOR_INHERITED(StartSystemServiceTimerCallback,
+ nsITimerCallback);
+ }
+
+ NS_IMETHOD Notify(nsITimer* aTimer) override
+ {
+ MOZ_ASSERT(NS_IsMainThread());
+
+ return StartSystemService(mSvcName.get(), mArgs.get());
+ }
+
+protected:
+ ~StartSystemServiceTimerCallback()
+ {
+ MOZ_COUNT_DTOR_INHERITED(StartSystemServiceTimerCallback,
+ nsITimerCallback);
+ }
+
+private:
+ nsCString mSvcName;
+ nsCString mArgs;
+};
+
+NS_IMPL_ISUPPORTS0(StartSystemServiceTimerCallback);
+
+nsresult
+StartSystemService(const char* aSvcName, const char* aArgs)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+
+ char value[PROPERTY_VALUE_MAX];
+ auto res = snprintf(value, sizeof(value), "%s:%s", aSvcName, aArgs);
+
+ if (res < 0) {
+ HAL_ERR("snprintf: %s", strerror(errno));
+ return NS_ERROR_FAILURE;
+ } else if (static_cast<size_t>(res) >= sizeof(value)) {
+ HAL_ERR("snprintf: trunctated service name %s", aSvcName);
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ if (NS_WARN_IF(property_set("ctl.start", value) < 0)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ /* If the system service is not running, re-try later to start it.
+ *
+ * This condition happens when we restart a service immediately
+ * after it crashed, as the service state remains 'stopping'
+ * instead of 'stopped'. Due to the limitation of property service,
+ * hereby add delay. See Bug 1143925 Comment 41.
+ */
+ if (!SystemServiceIsRunning(aSvcName)) {
+ nsCOMPtr<nsITimer> timer = do_CreateInstance("@mozilla.org/timer;1");
+ if (!timer) {
+ return NS_ERROR_FAILURE;
+ }
+
+ RefPtr<StartSystemServiceTimerCallback> timerCallback =
+ new StartSystemServiceTimerCallback(aSvcName, aArgs);
+
+ timer->InitWithCallback(timerCallback,
+ sRetryInterval,
+ nsITimer::TYPE_ONE_SHOT);
+ }
+
+ return NS_OK;
+}
+
+void
+StopSystemService(const char* aSvcName)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+
+ Unused << NS_WARN_IF(property_set("ctl.stop", aSvcName));
+}
+
+} // namespace hal_impl
+} // namespace mozilla
diff --git a/hal/gonk/UeventPoller.cpp b/hal/gonk/UeventPoller.cpp
new file mode 100644
index 000000000..3fbe850ed
--- /dev/null
+++ b/hal/gonk/UeventPoller.cpp
@@ -0,0 +1,312 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* Copyright 2012 Mozilla Foundation and Mozilla contributors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <pthread.h>
+#include <signal.h>
+#include <string.h>
+#include <strings.h>
+#include <unistd.h>
+
+#include <arpa/inet.h>
+#include <linux/types.h>
+#include <linux/netlink.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+
+#include "HalLog.h"
+#include "nsDebug.h"
+#include "base/message_loop.h"
+#include "base/task.h"
+#include "mozilla/ClearOnShutdown.h"
+#include "mozilla/FileUtils.h"
+#include "mozilla/Monitor.h"
+#include "nsThreadUtils.h"
+#include "nsXULAppAPI.h"
+
+#include "UeventPoller.h"
+
+using namespace mozilla;
+
+namespace mozilla {
+namespace hal_impl {
+
+static void ShutdownUevent();
+
+class NetlinkPoller : public MessageLoopForIO::Watcher
+{
+public:
+ NetlinkPoller() : mSocket(-1),
+ mIOLoop(MessageLoopForIO::current())
+ {
+ }
+
+ virtual ~NetlinkPoller() {}
+
+ bool OpenSocket();
+
+ virtual void OnFileCanReadWithoutBlocking(int fd);
+
+ // no writing to the netlink socket
+ virtual void OnFileCanWriteWithoutBlocking(int fd)
+ {
+ MOZ_CRASH("Must not write to netlink socket");
+ }
+
+ MessageLoopForIO *GetIOLoop () const { return mIOLoop; }
+ void RegisterObserver(IUeventObserver *aObserver)
+ {
+ mUeventObserverList.AddObserver(aObserver);
+ }
+
+ void UnregisterObserver(IUeventObserver *aObserver)
+ {
+ mUeventObserverList.RemoveObserver(aObserver);
+ if (mUeventObserverList.Length() == 0) {
+ ShutdownUevent(); // this will destroy self
+ }
+ }
+
+private:
+ ScopedClose mSocket;
+ MessageLoopForIO* mIOLoop;
+ MessageLoopForIO::FileDescriptorWatcher mReadWatcher;
+
+ const static int kBuffsize = 64 * 1024;
+ uint8_t mBuffer [kBuffsize];
+
+ typedef ObserverList<NetlinkEvent> UeventObserverList;
+ UeventObserverList mUeventObserverList;
+};
+
+bool
+NetlinkPoller::OpenSocket()
+{
+ mSocket.rwget() = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT);
+ if (mSocket.get() < 0) {
+ return false;
+ }
+
+ int sz = kBuffsize;
+
+ if (setsockopt(mSocket.get(), SOL_SOCKET, SO_RCVBUFFORCE, &sz,
+ sizeof(sz)) < 0) {
+ return false;
+ }
+
+ // add FD_CLOEXEC flag
+ int flags = fcntl(mSocket.get(), F_GETFD);
+ if (flags == -1) {
+ return false;
+ }
+ flags |= FD_CLOEXEC;
+ if (fcntl(mSocket.get(), F_SETFD, flags) == -1) {
+ return false;
+ }
+
+ // set non-blocking
+ if (fcntl(mSocket.get(), F_SETFL, O_NONBLOCK) == -1) {
+ return false;
+ }
+
+ struct sockaddr_nl saddr;
+ bzero(&saddr, sizeof(saddr));
+ saddr.nl_family = AF_NETLINK;
+ saddr.nl_groups = 1;
+ saddr.nl_pid = gettid();
+
+ do {
+ if (bind(mSocket.get(), (struct sockaddr *)&saddr, sizeof(saddr)) == 0) {
+ break;
+ }
+
+ if (errno != EADDRINUSE) {
+ return false;
+ }
+
+ if (saddr.nl_pid == 0) {
+ return false;
+ }
+
+ // Once there was any other place in the same process assigning saddr.nl_pid by
+ // gettid(), we can detect it and print warning message.
+ HAL_LOG("The netlink socket address saddr.nl_pid=%u is in use. "
+ "Let the kernel re-assign.\n", saddr.nl_pid);
+ saddr.nl_pid = 0;
+ } while (true);
+
+ if (!mIOLoop->WatchFileDescriptor(mSocket.get(),
+ true,
+ MessageLoopForIO::WATCH_READ,
+ &mReadWatcher,
+ this)) {
+ return false;
+ }
+
+ return true;
+}
+
+static StaticAutoPtr<NetlinkPoller> sPoller;
+
+class UeventInitTask : public Runnable
+{
+ NS_IMETHOD Run() override
+ {
+ if (!sPoller) {
+ return NS_OK;
+ }
+ if (sPoller->OpenSocket()) {
+ return NS_OK;
+ }
+ sPoller->GetIOLoop()->PostDelayedTask(MakeAndAddRef<UeventInitTask>(),
+ 1000);
+ return NS_OK;
+ }
+};
+
+void
+NetlinkPoller::OnFileCanReadWithoutBlocking(int fd)
+{
+ MOZ_ASSERT(fd == mSocket.get());
+ while (true) {
+ int ret = read(fd, mBuffer, kBuffsize);
+ if (ret == -1) {
+ if (errno == EAGAIN || errno == EWOULDBLOCK) {
+ return;
+ }
+ if (errno == EINTR) {
+ continue;
+ }
+ }
+ if (ret <= 0) {
+ // fatal error on netlink socket which should not happen
+ _exit(1);
+ }
+ NetlinkEvent netlinkEvent;
+ netlinkEvent.decode(reinterpret_cast<char*>(mBuffer), ret);
+ mUeventObserverList.Broadcast(netlinkEvent);
+ }
+}
+
+static bool sShutdown = false;
+
+class ShutdownNetlinkPoller;
+static StaticAutoPtr<ShutdownNetlinkPoller> sShutdownPoller;
+static Monitor* sMonitor = nullptr;
+
+class ShutdownNetlinkPoller {
+public:
+ ~ShutdownNetlinkPoller()
+ {
+ // This is called from KillClearOnShutdown() on the main thread.
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(XRE_GetIOMessageLoop());
+
+ {
+ MonitorAutoLock lock(*sMonitor);
+
+ XRE_GetIOMessageLoop()->PostTask(
+ NewRunnableFunction(ShutdownUeventIOThread));
+
+ while (!sShutdown) {
+ lock.Wait();
+ }
+ }
+
+ sShutdown = true;
+ delete sMonitor;
+ }
+
+ static void MaybeInit()
+ {
+ MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
+ if (sShutdown || sMonitor) {
+ // Don't init twice or init after shutdown.
+ return;
+ }
+
+ sMonitor = new Monitor("ShutdownNetlinkPoller.monitor");
+ {
+ ShutdownNetlinkPoller* shutdownPoller = new ShutdownNetlinkPoller();
+
+ nsCOMPtr<nsIRunnable> runnable = NS_NewRunnableFunction([=] () -> void
+ {
+ sShutdownPoller = shutdownPoller;
+ ClearOnShutdown(&sShutdownPoller); // Must run on the main thread.
+ });
+ MOZ_ASSERT(runnable);
+ MOZ_ALWAYS_SUCCEEDS(
+ NS_DispatchToMainThread(runnable, NS_DISPATCH_NORMAL));
+ }
+ }
+private:
+ ShutdownNetlinkPoller() = default;
+ static void ShutdownUeventIOThread()
+ {
+ MonitorAutoLock l(*sMonitor);
+ ShutdownUevent(); // Must run on the IO thread.
+ sShutdown = true;
+ l.NotifyAll();
+ }
+};
+
+static void
+InitializeUevent()
+{
+ MOZ_ASSERT(!sPoller);
+ sPoller = new NetlinkPoller();
+ sPoller->GetIOLoop()->PostTask(MakeAndAddRef<UeventInitTask>());
+
+ ShutdownNetlinkPoller::MaybeInit();
+}
+
+static void
+ShutdownUevent()
+{
+ sPoller = nullptr;
+}
+
+void
+RegisterUeventListener(IUeventObserver *aObserver)
+{
+ MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
+
+ if (sShutdown) {
+ return;
+ }
+
+ if (!sPoller) {
+ InitializeUevent();
+ }
+ sPoller->RegisterObserver(aObserver);
+}
+
+void
+UnregisterUeventListener(IUeventObserver *aObserver)
+{
+ MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
+
+ if (sShutdown) {
+ return;
+ }
+
+ sPoller->UnregisterObserver(aObserver);
+}
+
+} // hal_impl
+} // mozilla
+
diff --git a/hal/gonk/UeventPoller.h b/hal/gonk/UeventPoller.h
new file mode 100644
index 000000000..ba121cec2
--- /dev/null
+++ b/hal/gonk/UeventPoller.h
@@ -0,0 +1,49 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* Copyright 2012 Mozilla Foundation and Mozilla contributors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _mozilla_uevent_poller_h_
+#define _mozilla_uevent_poller_h_
+
+#include <sysutils/NetlinkEvent.h>
+#include "mozilla/Observer.h"
+
+class NetlinkEvent;
+
+namespace mozilla {
+namespace hal_impl {
+
+typedef mozilla::Observer<NetlinkEvent> IUeventObserver;
+
+/**
+ * Register for uevent notification. Note that the method should run on the
+ * <b> IO Thread </b>
+ * @aObserver the observer to be added. The observer's Notify() is only called
+ * on the <b> IO Thread </b>
+ */
+void RegisterUeventListener(IUeventObserver *aObserver);
+
+/**
+ * Unregister for uevent notification. Note that the method should run on the
+ * <b> IO Thread </b>
+ * @aObserver the observer to be removed
+ */
+void UnregisterUeventListener(IUeventObserver *aObserver);
+
+}
+}
+
+#endif
+
diff --git a/hal/gonk/fanotify.h b/hal/gonk/fanotify.h
new file mode 100644
index 000000000..e715d3bf9
--- /dev/null
+++ b/hal/gonk/fanotify.h
@@ -0,0 +1,118 @@
+#ifndef _LINUX_FANOTIFY_H
+#define _LINUX_FANOTIFY_H
+
+/* This is a Linux header generated by "make headers_install" */
+
+#include <linux/types.h>
+
+/* the following events that user-space can register for */
+#define FAN_ACCESS 0x00000001 /* File was accessed */
+#define FAN_MODIFY 0x00000002 /* File was modified */
+#define FAN_CLOSE_WRITE 0x00000008 /* Writtable file closed */
+#define FAN_CLOSE_NOWRITE 0x00000010 /* Unwrittable file closed */
+#define FAN_OPEN 0x00000020 /* File was opened */
+
+#define FAN_Q_OVERFLOW 0x00004000 /* Event queued overflowed */
+
+#define FAN_OPEN_PERM 0x00010000 /* File open in perm check */
+#define FAN_ACCESS_PERM 0x00020000 /* File accessed in perm check */
+
+#define FAN_ONDIR 0x40000000 /* event occurred against dir */
+
+#define FAN_EVENT_ON_CHILD 0x08000000 /* interested in child events */
+
+/* helper events */
+#define FAN_CLOSE (FAN_CLOSE_WRITE | FAN_CLOSE_NOWRITE) /* close */
+
+/* flags used for fanotify_init() */
+#define FAN_CLOEXEC 0x00000001
+#define FAN_NONBLOCK 0x00000002
+
+/* These are NOT bitwise flags. Both bits are used togther. */
+#define FAN_CLASS_NOTIF 0x00000000
+#define FAN_CLASS_CONTENT 0x00000004
+#define FAN_CLASS_PRE_CONTENT 0x00000008
+#define FAN_ALL_CLASS_BITS (FAN_CLASS_NOTIF | FAN_CLASS_CONTENT | \
+ FAN_CLASS_PRE_CONTENT)
+
+#define FAN_UNLIMITED_QUEUE 0x00000010
+#define FAN_UNLIMITED_MARKS 0x00000020
+
+#define FAN_ALL_INIT_FLAGS (FAN_CLOEXEC | FAN_NONBLOCK | \
+ FAN_ALL_CLASS_BITS | FAN_UNLIMITED_QUEUE |\
+ FAN_UNLIMITED_MARKS)
+
+/* flags used for fanotify_modify_mark() */
+#define FAN_MARK_ADD 0x00000001
+#define FAN_MARK_REMOVE 0x00000002
+#define FAN_MARK_DONT_FOLLOW 0x00000004
+#define FAN_MARK_ONLYDIR 0x00000008
+#define FAN_MARK_MOUNT 0x00000010
+#define FAN_MARK_IGNORED_MASK 0x00000020
+#define FAN_MARK_IGNORED_SURV_MODIFY 0x00000040
+#define FAN_MARK_FLUSH 0x00000080
+
+#define FAN_ALL_MARK_FLAGS (FAN_MARK_ADD |\
+ FAN_MARK_REMOVE |\
+ FAN_MARK_DONT_FOLLOW |\
+ FAN_MARK_ONLYDIR |\
+ FAN_MARK_MOUNT |\
+ FAN_MARK_IGNORED_MASK |\
+ FAN_MARK_IGNORED_SURV_MODIFY |\
+ FAN_MARK_FLUSH)
+
+/*
+ * All of the events - we build the list by hand so that we can add flags in
+ * the future and not break backward compatibility. Apps will get only the
+ * events that they originally wanted. Be sure to add new events here!
+ */
+#define FAN_ALL_EVENTS (FAN_ACCESS |\
+ FAN_MODIFY |\
+ FAN_CLOSE |\
+ FAN_OPEN)
+
+/*
+ * All events which require a permission response from userspace
+ */
+#define FAN_ALL_PERM_EVENTS (FAN_OPEN_PERM |\
+ FAN_ACCESS_PERM)
+
+#define FAN_ALL_OUTGOING_EVENTS (FAN_ALL_EVENTS |\
+ FAN_ALL_PERM_EVENTS |\
+ FAN_Q_OVERFLOW)
+
+#define FANOTIFY_METADATA_VERSION 3
+
+struct fanotify_event_metadata {
+ __u32 event_len;
+ __u8 vers;
+ __u8 reserved;
+ __u16 metadata_len;
+ __u64 mask;
+ __s32 fd;
+ __s32 pid;
+};
+
+struct fanotify_response {
+ __s32 fd;
+ __u32 response;
+};
+
+/* Legit userspace responses to a _PERM event */
+#define FAN_ALLOW 0x01
+#define FAN_DENY 0x02
+/* No fd set in event */
+#define FAN_NOFD -1
+
+/* Helper functions to deal with fanotify_event_metadata buffers */
+#define FAN_EVENT_METADATA_LEN (sizeof(struct fanotify_event_metadata))
+
+#define FAN_EVENT_NEXT(meta, len) ((len) -= (meta)->event_len, \
+ (struct fanotify_event_metadata*)(((char *)(meta)) + \
+ (meta)->event_len))
+
+#define FAN_EVENT_OK(meta, len) ((long)(len) >= (long)FAN_EVENT_METADATA_LEN && \
+ (long)(meta)->event_len >= (long)FAN_EVENT_METADATA_LEN && \
+ (long)(meta)->event_len <= (long)(len))
+
+#endif /* _LINUX_FANOTIFY_H */
diff --git a/hal/gonk/nsIRecoveryService.idl b/hal/gonk/nsIRecoveryService.idl
new file mode 100644
index 000000000..ecbb39c0e
--- /dev/null
+++ b/hal/gonk/nsIRecoveryService.idl
@@ -0,0 +1,39 @@
+/* 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 "nsISupports.idl"
+
+[scriptable, uuid(bc24fb33-a0c1-49ca-aa43-05f167e02fb6)]
+interface nsIRecoveryService : nsISupports
+{
+ /**
+ * Possible values of fotaStatus.result. These should stay in sync with
+ * librecovery/librecovery.h
+ */
+ const long FOTA_UPDATE_UNKNOWN = 0;
+ const long FOTA_UPDATE_FAIL = 1;
+ const long FOTA_UPDATE_SUCCESS = 2;
+
+ /**
+ * Uses recovery to wipe the data and cache partitions. If this call is
+ * successful, the device should reboot before the function call ever returns.
+ *
+ * @throws NS_ERROR_FAILURE when rebooting into recovery fails for some reason.
+ */
+ void factoryReset(in string reason);
+
+ /**
+ * Use recovery to install an OTA update.zip. If this call is
+ * successful, the device should reboot before the function call ever returns.
+ *
+ * @throws NS_ERROR_FAILURE when rebooting into recovery fails for some reason.
+ */
+ void installFotaUpdate(in string updatePath);
+
+ /**
+ * @return The status of the last FOTA update. One of FOTA_UPDATE_UNKNOWN,
+ * FOTA_UPDATE_FAIL, FOTA_UPDATE_SUCCESS.
+ */
+ long getFotaUpdateStatus();
+};
diff --git a/hal/gonk/tavarua.h b/hal/gonk/tavarua.h
new file mode 100644
index 000000000..4eb3483a8
--- /dev/null
+++ b/hal/gonk/tavarua.h
@@ -0,0 +1,484 @@
+#ifndef __LINUX_TAVARUA_H
+#define __LINUX_TAVARUA_H
+
+/* This is a Linux header generated by "make headers_install" */
+
+#include <stdint.h>
+#include <linux/ioctl.h>
+#include <linux/videodev2.h>
+
+
+#undef FM_DEBUG
+
+/* constants */
+#define RDS_BLOCKS_NUM (4)
+#define BYTES_PER_BLOCK (3)
+#define MAX_PS_LENGTH (96)
+#define MAX_RT_LENGTH (64)
+
+#define XFRDAT0 (0x20)
+#define XFRDAT1 (0x21)
+#define XFRDAT2 (0x22)
+
+#define INTDET_PEEK_MSB (0x88)
+#define INTDET_PEEK_LSB (0x26)
+
+#define RMSSI_PEEK_MSB (0x88)
+#define RMSSI_PEEK_LSB (0xA8)
+
+#define MPX_DCC_BYPASS_POKE_MSB (0x88)
+#define MPX_DCC_BYPASS_POKE_LSB (0xC0)
+
+#define MPX_DCC_PEEK_MSB_REG1 (0x88)
+#define MPX_DCC_PEEK_LSB_REG1 (0xC2)
+
+#define MPX_DCC_PEEK_MSB_REG2 (0x88)
+#define MPX_DCC_PEEK_LSB_REG2 (0xC3)
+
+#define MPX_DCC_PEEK_MSB_REG3 (0x88)
+#define MPX_DCC_PEEK_LSB_REG3 (0xC4)
+
+#define ON_CHANNEL_TH_MSB (0x0B)
+#define ON_CHANNEL_TH_LSB (0xA8)
+
+#define OFF_CHANNEL_TH_MSB (0x0B)
+#define OFF_CHANNEL_TH_LSB (0xAC)
+
+#define ENF_200Khz (1)
+#define SRCH200KHZ_OFFSET (7)
+#define SRCH_MASK (1 << SRCH200KHZ_OFFSET)
+
+/* Standard buffer size */
+#define STD_BUF_SIZE (128)
+/* Search direction */
+#define SRCH_DIR_UP (0)
+#define SRCH_DIR_DOWN (1)
+
+/* control options */
+#define CTRL_ON (1)
+#define CTRL_OFF (0)
+
+#define US_LOW_BAND (87.5)
+#define US_HIGH_BAND (108)
+
+/* constant for Tx */
+
+#define MASK_PI (0x0000FFFF)
+#define MASK_PI_MSB (0x0000FF00)
+#define MASK_PI_LSB (0x000000FF)
+#define MASK_PTY (0x0000001F)
+#define MASK_TXREPCOUNT (0x0000000F)
+
+#undef FMDBG
+#ifdef FM_DEBUG
+ #define FMDBG(fmt, args...) printk(KERN_INFO "tavarua_radio: " fmt, ##args)
+#else
+ #define FMDBG(fmt, args...)
+#endif
+
+#undef FMDERR
+#define FMDERR(fmt, args...) printk(KERN_INFO "tavarua_radio: " fmt, ##args)
+
+#undef FMDBG_I2C
+#ifdef FM_DEBUG_I2C
+ #define FMDBG_I2C(fmt, args...) printk(KERN_INFO "fm_i2c: " fmt, ##args)
+#else
+ #define FMDBG_I2C(fmt, args...)
+#endif
+
+/* function declarations */
+/* FM Core audio paths. */
+#define TAVARUA_AUDIO_OUT_ANALOG_OFF (0)
+#define TAVARUA_AUDIO_OUT_ANALOG_ON (1)
+#define TAVARUA_AUDIO_OUT_DIGITAL_OFF (0)
+#define TAVARUA_AUDIO_OUT_DIGITAL_ON (1)
+
+int tavarua_set_audio_path(int digital_on, int analog_on);
+
+/* defines and enums*/
+
+#define MARIMBA_A0 0x01010013
+#define MARIMBA_2_1 0x02010204
+#define BAHAMA_1_0 0x0302010A
+#define BAHAMA_2_0 0x04020205
+#define WAIT_TIMEOUT 2000
+#define RADIO_INIT_TIME 15
+#define TAVARUA_DELAY 10
+/*
+ * The frequency is set in units of 62.5 Hz when using V4L2_TUNER_CAP_LOW,
+ * 62.5 kHz otherwise.
+ * The tuner is able to have a channel spacing of 50, 100 or 200 kHz.
+ * tuner->capability is therefore set to V4L2_TUNER_CAP_LOW
+ * The FREQ_MUL is then: 1 MHz / 62.5 Hz = 16000
+ */
+#define FREQ_MUL (1000000 / 62.5)
+
+enum v4l2_cid_private_tavarua_t {
+ V4L2_CID_PRIVATE_TAVARUA_SRCHMODE = (V4L2_CID_PRIVATE_BASE + 1),
+ V4L2_CID_PRIVATE_TAVARUA_SCANDWELL,
+ V4L2_CID_PRIVATE_TAVARUA_SRCHON,
+ V4L2_CID_PRIVATE_TAVARUA_STATE,
+ V4L2_CID_PRIVATE_TAVARUA_TRANSMIT_MODE,
+ V4L2_CID_PRIVATE_TAVARUA_RDSGROUP_MASK,
+ V4L2_CID_PRIVATE_TAVARUA_REGION,
+ V4L2_CID_PRIVATE_TAVARUA_SIGNAL_TH,
+ V4L2_CID_PRIVATE_TAVARUA_SRCH_PTY,
+ V4L2_CID_PRIVATE_TAVARUA_SRCH_PI,
+ V4L2_CID_PRIVATE_TAVARUA_SRCH_CNT,
+ V4L2_CID_PRIVATE_TAVARUA_EMPHASIS,
+ V4L2_CID_PRIVATE_TAVARUA_RDS_STD,
+ V4L2_CID_PRIVATE_TAVARUA_SPACING,
+ V4L2_CID_PRIVATE_TAVARUA_RDSON,
+ V4L2_CID_PRIVATE_TAVARUA_RDSGROUP_PROC,
+ V4L2_CID_PRIVATE_TAVARUA_LP_MODE,
+ V4L2_CID_PRIVATE_TAVARUA_ANTENNA,
+ V4L2_CID_PRIVATE_TAVARUA_RDSD_BUF,
+ V4L2_CID_PRIVATE_TAVARUA_PSALL,
+ /*v4l2 Tx controls*/
+ V4L2_CID_PRIVATE_TAVARUA_TX_SETPSREPEATCOUNT,
+ V4L2_CID_PRIVATE_TAVARUA_STOP_RDS_TX_PS_NAME,
+ V4L2_CID_PRIVATE_TAVARUA_STOP_RDS_TX_RT,
+ V4L2_CID_PRIVATE_TAVARUA_IOVERC,
+ V4L2_CID_PRIVATE_TAVARUA_INTDET,
+ V4L2_CID_PRIVATE_TAVARUA_MPX_DCC,
+ V4L2_CID_PRIVATE_TAVARUA_AF_JUMP,
+ V4L2_CID_PRIVATE_TAVARUA_RSSI_DELTA,
+ V4L2_CID_PRIVATE_TAVARUA_HLSI,
+
+ /*
+ * Here we have IOCTl's that are specific to IRIS
+ * (V4L2_CID_PRIVATE_BASE + 0x1E to V4L2_CID_PRIVATE_BASE + 0x28)
+ */
+ V4L2_CID_PRIVATE_SOFT_MUTE,/* 0x800001E*/
+ V4L2_CID_PRIVATE_RIVA_ACCS_ADDR,
+ V4L2_CID_PRIVATE_RIVA_ACCS_LEN,
+ V4L2_CID_PRIVATE_RIVA_PEEK,
+ V4L2_CID_PRIVATE_RIVA_POKE,
+ V4L2_CID_PRIVATE_SSBI_ACCS_ADDR,
+ V4L2_CID_PRIVATE_SSBI_PEEK,
+ V4L2_CID_PRIVATE_SSBI_POKE,
+ V4L2_CID_PRIVATE_TX_TONE,
+ V4L2_CID_PRIVATE_RDS_GRP_COUNTERS,
+ V4L2_CID_PRIVATE_SET_NOTCH_FILTER,/* 0x8000028 */
+
+ V4L2_CID_PRIVATE_TAVARUA_SET_AUDIO_PATH,/* 0x8000029 */
+ V4L2_CID_PRIVATE_TAVARUA_DO_CALIBRATION,/* 0x800002A : IRIS */
+ V4L2_CID_PRIVATE_TAVARUA_SRCH_ALGORITHM,/* 0x800002B */
+ V4L2_CID_PRIVATE_IRIS_GET_SINR, /* 0x800002C : IRIS */
+ V4L2_CID_PRIVATE_INTF_LOW_THRESHOLD, /* 0x800002D */
+ V4L2_CID_PRIVATE_INTF_HIGH_THRESHOLD, /* 0x800002E */
+ V4L2_CID_PRIVATE_SINR_THRESHOLD, /* 0x800002F : IRIS */
+ V4L2_CID_PRIVATE_SINR_SAMPLES, /* 0x8000030 : IRIS */
+
+};
+
+enum tavarua_buf_t {
+ TAVARUA_BUF_SRCH_LIST,
+ TAVARUA_BUF_EVENTS,
+ TAVARUA_BUF_RT_RDS,
+ TAVARUA_BUF_PS_RDS,
+ TAVARUA_BUF_RAW_RDS,
+ TAVARUA_BUF_AF_LIST,
+ TAVARUA_BUF_MAX
+};
+
+enum tavarua_xfr_t {
+ TAVARUA_XFR_SYNC,
+ TAVARUA_XFR_ERROR,
+ TAVARUA_XFR_SRCH_LIST,
+ TAVARUA_XFR_RT_RDS,
+ TAVARUA_XFR_PS_RDS,
+ TAVARUA_XFR_AF_LIST,
+ TAVARUA_XFR_MAX
+};
+
+enum channel_spacing {
+ FM_CH_SPACE_200KHZ,
+ FM_CH_SPACE_100KHZ,
+ FM_CH_SPACE_50KHZ
+};
+
+enum step_size {
+ NO_SRCH200khz,
+ ENF_SRCH200khz
+};
+
+enum emphasis {
+ EMP_75,
+ EMP_50
+};
+
+enum rds_std {
+ RBDS_STD,
+ RDS_STD
+};
+
+/* offsets */
+#define RAW_RDS 0x0F
+#define RDS_BLOCK 3
+
+/* registers*/
+#define MARIMBA_XO_BUFF_CNTRL 0x07
+#define RADIO_REGISTERS 0x30
+#define XFR_REG_NUM 16
+#define STATUS_REG_NUM 3
+
+/* TX constants */
+#define HEADER_SIZE 4
+#define TX_ON 0x80
+#define TAVARUA_TX_RT RDS_RT_0
+#define TAVARUA_TX_PS RDS_PS_0
+
+enum register_t {
+ STATUS_REG1 = 0,
+ STATUS_REG2,
+ STATUS_REG3,
+ RDCTRL,
+ FREQ,
+ TUNECTRL,
+ SRCHRDS1,
+ SRCHRDS2,
+ SRCHCTRL,
+ IOCTRL,
+ RDSCTRL,
+ ADVCTRL,
+ AUDIOCTRL,
+ RMSSI,
+ IOVERC,
+ AUDIOIND = 0x1E,
+ XFRCTRL,
+ FM_CTL0 = 0xFF,
+ LEAKAGE_CNTRL = 0xFE,
+};
+#define BAHAMA_RBIAS_CTL1 0x07
+#define BAHAMA_FM_MODE_REG 0xFD
+#define BAHAMA_FM_CTL1_REG 0xFE
+#define BAHAMA_FM_CTL0_REG 0xFF
+#define BAHAMA_FM_MODE_NORMAL 0x00
+#define BAHAMA_LDO_DREG_CTL0 0xF0
+#define BAHAMA_LDO_AREG_CTL0 0xF4
+
+/* Radio Control */
+#define RDCTRL_STATE_OFFSET 0
+#define RDCTRL_STATE_MASK (3 << RDCTRL_STATE_OFFSET)
+#define RDCTRL_BAND_OFFSET 2
+#define RDCTRL_BAND_MASK (1 << RDCTRL_BAND_OFFSET)
+#define RDCTRL_CHSPACE_OFFSET 3
+#define RDCTRL_CHSPACE_MASK (3 << RDCTRL_CHSPACE_OFFSET)
+#define RDCTRL_DEEMPHASIS_OFFSET 5
+#define RDCTRL_DEEMPHASIS_MASK (1 << RDCTRL_DEEMPHASIS_OFFSET)
+#define RDCTRL_HLSI_OFFSET 6
+#define RDCTRL_HLSI_MASK (3 << RDCTRL_HLSI_OFFSET)
+#define RDSAF_OFFSET 6
+#define RDSAF_MASK (1 << RDSAF_OFFSET)
+
+/* Tune Control */
+#define TUNE_STATION 0x01
+#define ADD_OFFSET (1 << 1)
+#define SIGSTATE (1 << 5)
+#define MOSTSTATE (1 << 6)
+#define RDSSYNC (1 << 7)
+/* Search Control */
+#define SRCH_MODE_OFFSET 0
+#define SRCH_MODE_MASK (7 << SRCH_MODE_OFFSET)
+#define SRCH_DIR_OFFSET 3
+#define SRCH_DIR_MASK (1 << SRCH_DIR_OFFSET)
+#define SRCH_DWELL_OFFSET 4
+#define SRCH_DWELL_MASK (7 << SRCH_DWELL_OFFSET)
+#define SRCH_STATE_OFFSET 7
+#define SRCH_STATE_MASK (1 << SRCH_STATE_OFFSET)
+
+/* I/O Control */
+#define IOC_HRD_MUTE 0x03
+#define IOC_SFT_MUTE (1 << 2)
+#define IOC_MON_STR (1 << 3)
+#define IOC_SIG_BLND (1 << 4)
+#define IOC_INTF_BLND (1 << 5)
+#define IOC_ANTENNA (1 << 6)
+#define IOC_ANTENNA_OFFSET 6
+#define IOC_ANTENNA_MASK (1 << IOC_ANTENNA_OFFSET)
+
+/* RDS Control */
+#define RDS_ON 0x01
+#define RDSCTRL_STANDARD_OFFSET 1
+#define RDSCTRL_STANDARD_MASK (1 << RDSCTRL_STANDARD_OFFSET)
+
+/* Advanced features controls */
+#define RDSRTEN (1 << 3)
+#define RDSPSEN (1 << 4)
+
+/* Audio path control */
+#define AUDIORX_ANALOG_OFFSET 0
+#define AUDIORX_ANALOG_MASK (1 << AUDIORX_ANALOG_OFFSET)
+#define AUDIORX_DIGITAL_OFFSET 1
+#define AUDIORX_DIGITAL_MASK (1 << AUDIORX_DIGITAL_OFFSET)
+#define AUDIOTX_OFFSET 2
+#define AUDIOTX_MASK (1 << AUDIOTX_OFFSET)
+#define I2SCTRL_OFFSET 3
+#define I2SCTRL_MASK (1 << I2SCTRL_OFFSET)
+
+/* Search options */
+enum search_t {
+ SEEK,
+ SCAN,
+ SCAN_FOR_STRONG,
+ SCAN_FOR_WEAK,
+ RDS_SEEK_PTY,
+ RDS_SCAN_PTY,
+ RDS_SEEK_PI,
+ RDS_AF_JUMP,
+};
+
+enum audio_path {
+ FM_DIGITAL_PATH,
+ FM_ANALOG_PATH
+};
+#define SRCH_MODE 0x07
+#define SRCH_DIR 0x08 /* 0-up 1-down */
+#define SCAN_DWELL 0x70
+#define SRCH_ON 0x80
+
+/* RDS CONFIG */
+#define RDS_CONFIG_PSALL 0x01
+
+#define FM_ENABLE 0x22
+#define SET_REG_FIELD(reg, val, offset, mask) \
+ (reg = (reg & ~mask) | (((val) << offset) & mask))
+#define GET_REG_FIELD(reg, offset, mask) ((reg & mask) >> offset)
+#define RSH_DATA(val, offset) ((val) >> (offset))
+#define LSH_DATA(val, offset) ((val) << (offset))
+#define GET_ABS_VAL(val) ((val) & (0xFF))
+
+enum radio_state_t {
+ FM_OFF,
+ FM_RECV,
+ FM_TRANS,
+ FM_RESET,
+};
+
+#define XFRCTRL_WRITE (1 << 7)
+
+/* Interrupt status */
+
+/* interrupt register 1 */
+#define READY (1 << 0) /* Radio ready after powerup or reset */
+#define TUNE (1 << 1) /* Tune completed */
+#define SEARCH (1 << 2) /* Search completed (read FREQ) */
+#define SCANNEXT (1 << 3) /* Scanning for next station */
+#define SIGNAL (1 << 4) /* Signal indicator change (read SIGSTATE) */
+#define INTF (1 << 5) /* Interference cnt has fallen outside range */
+#define SYNC (1 << 6) /* RDS sync state change (read RDSSYNC) */
+#define AUDIO (1 << 7) /* Audio Control indicator (read AUDIOIND) */
+
+/* interrupt register 2 */
+#define RDSDAT (1 << 0) /* New unread RDS data group available */
+#define BLOCKB (1 << 1) /* Block-B match condition exists */
+#define PROGID (1 << 2) /* Block-A or Block-C matched stored PI value*/
+#define RDSPS (1 << 3) /* New RDS Program Service Table available */
+#define RDSRT (1 << 4) /* New RDS Radio Text available */
+#define RDSAF (1 << 5) /* New RDS AF List available */
+#define TXRDSDAT (1 << 6) /* Transmitted an RDS group */
+#define TXRDSDONE (1 << 7) /* RDS raw group one-shot transmit completed */
+
+/* interrupt register 3 */
+#define TRANSFER (1 << 0) /* Data transfer (XFR) completed */
+#define RDSPROC (1 << 1) /* Dynamic RDS Processing complete */
+#define ERROR (1 << 7) /* Err occurred.Read code to determine cause */
+
+
+#define FM_TX_PWR_LVL_0 0 /* Lowest power lvl that can be set for Tx */
+#define FM_TX_PWR_LVL_MAX 7 /* Max power lvl for Tx */
+/* Transfer */
+enum tavarua_xfr_ctrl_t {
+ RDS_PS_0 = 0x01,
+ RDS_PS_1,
+ RDS_PS_2,
+ RDS_PS_3,
+ RDS_PS_4,
+ RDS_PS_5,
+ RDS_PS_6,
+ RDS_RT_0,
+ RDS_RT_1,
+ RDS_RT_2,
+ RDS_RT_3,
+ RDS_RT_4,
+ RDS_AF_0,
+ RDS_AF_1,
+ RDS_CONFIG,
+ RDS_TX_GROUPS,
+ RDS_COUNT_0,
+ RDS_COUNT_1,
+ RDS_COUNT_2,
+ RADIO_CONFIG,
+ RX_CONFIG,
+ RX_TIMERS,
+ RX_STATIONS_0,
+ RX_STATIONS_1,
+ INT_CTRL,
+ ERROR_CODE,
+ CHIPID,
+ CAL_DAT_0 = 0x20,
+ CAL_DAT_1,
+ CAL_DAT_2,
+ CAL_DAT_3,
+ CAL_CFG_0,
+ CAL_CFG_1,
+ DIG_INTF_0,
+ DIG_INTF_1,
+ DIG_AGC_0,
+ DIG_AGC_1,
+ DIG_AGC_2,
+ DIG_AUDIO_0,
+ DIG_AUDIO_1,
+ DIG_AUDIO_2,
+ DIG_AUDIO_3,
+ DIG_AUDIO_4,
+ DIG_RXRDS,
+ DIG_DCC,
+ DIG_SPUR,
+ DIG_MPXDCC,
+ DIG_PILOT,
+ DIG_DEMOD,
+ DIG_MOST,
+ DIG_TX_0,
+ DIG_TX_1,
+ PHY_TXGAIN = 0x3B,
+ PHY_CONFIG,
+ PHY_TXBLOCK,
+ PHY_TCB,
+ XFR_PEEK_MODE = 0x40,
+ XFR_POKE_MODE = 0xC0,
+ TAVARUA_XFR_CTRL_MAX
+};
+
+enum tavarua_evt_t {
+ TAVARUA_EVT_RADIO_READY,
+ TAVARUA_EVT_TUNE_SUCC,
+ TAVARUA_EVT_SEEK_COMPLETE,
+ TAVARUA_EVT_SCAN_NEXT,
+ TAVARUA_EVT_NEW_RAW_RDS,
+ TAVARUA_EVT_NEW_RT_RDS,
+ TAVARUA_EVT_NEW_PS_RDS,
+ TAVARUA_EVT_ERROR,
+ TAVARUA_EVT_BELOW_TH,
+ TAVARUA_EVT_ABOVE_TH,
+ TAVARUA_EVT_STEREO,
+ TAVARUA_EVT_MONO,
+ TAVARUA_EVT_RDS_AVAIL,
+ TAVARUA_EVT_RDS_NOT_AVAIL,
+ TAVARUA_EVT_NEW_SRCH_LIST,
+ TAVARUA_EVT_NEW_AF_LIST,
+ TAVARUA_EVT_TXRDSDAT,
+ TAVARUA_EVT_TXRDSDONE,
+ TAVARUA_EVT_RADIO_DISABLED
+};
+
+enum tavarua_region_t {
+ TAVARUA_REGION_US,
+ TAVARUA_REGION_EU,
+ TAVARUA_REGION_JAPAN,
+ TAVARUA_REGION_JAPAN_WIDE,
+ TAVARUA_REGION_OTHER
+};
+
+#endif /* __LINUX_TAVARUA_H */
diff --git a/hal/linux/LinuxMemory.cpp b/hal/linux/LinuxMemory.cpp
new file mode 100644
index 000000000..14ed63146
--- /dev/null
+++ b/hal/linux/LinuxMemory.cpp
@@ -0,0 +1,39 @@
+/* -*- 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 <stdio.h>
+#include "Hal.h"
+
+namespace mozilla {
+namespace hal_impl {
+
+uint32_t
+GetTotalSystemMemory()
+{
+ static uint32_t sTotalMemory;
+ static bool sTotalMemoryObtained = false;
+
+ if (!sTotalMemoryObtained) {
+ sTotalMemoryObtained = true;
+
+ FILE* fd = fopen("/proc/meminfo", "r");
+ if (!fd) {
+ return 0;
+ }
+
+ int rv = fscanf(fd, "MemTotal: %i kB", &sTotalMemory);
+
+ if (fclose(fd) || rv != 1) {
+ return 0;
+ }
+ }
+
+ return sTotalMemory * 1024;
+}
+
+} // namespace hal_impl
+} // namespace mozilla
diff --git a/hal/linux/LinuxPower.cpp b/hal/linux/LinuxPower.cpp
new file mode 100644
index 000000000..70ab4a3f6
--- /dev/null
+++ b/hal/linux/LinuxPower.cpp
@@ -0,0 +1,167 @@
+/* -*- 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 <unistd.h>
+#include <sys/reboot.h>
+#include "nsIObserverService.h"
+#include "mozilla/Services.h"
+#include "MainThreadUtils.h"
+
+#if defined(MOZ_WIDGET_GONK)
+#include "cutils/android_reboot.h"
+#include "cutils/properties.h"
+#endif
+
+namespace mozilla {
+namespace hal_impl {
+
+#if (defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 19)
+static void
+PowerCtl(const char* aValue, int aCmd)
+{
+ // this invokes init's powerctl builtin via /init.rc
+ property_set("sys.powerctl", aValue);
+ // device should reboot in few moments, but if it doesn't - call
+ // android_reboot() to make sure that init isn't stuck somewhere
+ sleep(10);
+ HAL_LOG("Powerctl call takes too long, forcing %s.", aValue);
+ android_reboot(aCmd, 0, nullptr);
+}
+#endif
+
+void
+Reboot()
+{
+ if (NS_IsMainThread()) {
+ nsCOMPtr<nsIObserverService> obsServ = services::GetObserverService();
+ if (obsServ) {
+ obsServ->NotifyObservers(nullptr, "system-reboot", nullptr);
+ }
+ }
+
+#if !defined(MOZ_WIDGET_GONK)
+ sync();
+ reboot(RB_AUTOBOOT);
+#elif (ANDROID_VERSION < 19)
+ android_reboot(ANDROID_RB_RESTART, 0, nullptr);
+#else
+ PowerCtl("reboot", ANDROID_RB_RESTART);
+#endif
+}
+
+void
+PowerOff()
+{
+ if (NS_IsMainThread()) {
+ nsCOMPtr<nsIObserverService> obsServ = services::GetObserverService();
+ if (obsServ) {
+ obsServ->NotifyObservers(nullptr, "system-power-off", nullptr);
+ }
+ }
+
+#if !defined(MOZ_WIDGET_GONK)
+ sync();
+ reboot(RB_POWER_OFF);
+#elif (ANDROID_VERSION < 19)
+ android_reboot(ANDROID_RB_POWEROFF, 0, nullptr);
+#else
+ PowerCtl("shutdown", ANDROID_RB_POWEROFF);
+#endif
+}
+
+// Structure to specify how watchdog pthread is going to work.
+typedef struct watchdogParam
+{
+ hal::ShutdownMode mode; // Specify how to shutdown the system.
+ int32_t timeoutSecs; // Specify the delayed seconds to shutdown the system.
+
+ watchdogParam(hal::ShutdownMode aMode, int32_t aTimeoutSecs)
+ : mode(aMode), timeoutSecs(aTimeoutSecs) {}
+} watchdogParam_t;
+
+// Function to complusively shut down the system with a given mode.
+static void
+QuitHard(hal::ShutdownMode aMode)
+{
+ switch (aMode)
+ {
+ case hal::eHalShutdownMode_PowerOff:
+ PowerOff();
+ break;
+ case hal::eHalShutdownMode_Reboot:
+ Reboot();
+ break;
+ case hal::eHalShutdownMode_Restart:
+ // Don't let signal handlers affect forced shutdown.
+ kill(0, SIGKILL);
+ // If we can't SIGKILL our process group, something is badly
+ // wrong. Trying to deliver a catch-able signal to ourselves can
+ // invoke signal handlers and might cause problems. So try
+ // _exit() and hope we go away.
+ _exit(1);
+ break;
+ default:
+ MOZ_CRASH();
+ }
+}
+
+// Function to complusively shut down the system with a given mode when timeout.
+static void*
+ForceQuitWatchdog(void* aParamPtr)
+{
+ watchdogParam_t* paramPtr = reinterpret_cast<watchdogParam_t*>(aParamPtr);
+ if (paramPtr->timeoutSecs > 0 && paramPtr->timeoutSecs <= 30) {
+ // If we shut down normally before the timeout, this thread will
+ // be harmlessly reaped by the OS.
+ TimeStamp deadline =
+ (TimeStamp::Now() + TimeDuration::FromSeconds(paramPtr->timeoutSecs));
+ while (true) {
+ TimeDuration remaining = (deadline - TimeStamp::Now());
+ int sleepSeconds = int(remaining.ToSeconds());
+ if (sleepSeconds <= 0) {
+ break;
+ }
+ sleep(sleepSeconds);
+ }
+ }
+ hal::ShutdownMode mode = paramPtr->mode;
+ delete paramPtr;
+ QuitHard(mode);
+ return nullptr;
+}
+
+void
+StartForceQuitWatchdog(hal::ShutdownMode aMode, int32_t aTimeoutSecs)
+{
+ // Force-quits are intepreted a little more ferociously on Gonk,
+ // because while Gecko is in the process of shutting down, the user
+ // can't call 911, for example. And if we hang on shutdown, bad
+ // things happen. So, make sure that doesn't happen.
+ if (aTimeoutSecs <= 0) {
+ return;
+ }
+
+ // Use a raw pthread here to insulate ourselves from bugs in other
+ // Gecko code that we're trying to protect!
+ //
+ // Note that we let the watchdog in charge of releasing |paramPtr|
+ // if the pthread is successfully created.
+ watchdogParam_t* paramPtr = new watchdogParam_t(aMode, aTimeoutSecs);
+ pthread_t watchdog;
+ if (pthread_create(&watchdog, nullptr,
+ ForceQuitWatchdog,
+ reinterpret_cast<void*>(paramPtr))) {
+ // Better safe than sorry.
+ delete paramPtr;
+ QuitHard(aMode);
+ }
+ // The watchdog thread is off and running now.
+}
+
+} // hal_impl
+} // mozilla
diff --git a/hal/linux/UPowerClient.cpp b/hal/linux/UPowerClient.cpp
new file mode 100644
index 000000000..9f6e04379
--- /dev/null
+++ b/hal/linux/UPowerClient.cpp
@@ -0,0 +1,508 @@
+/* -*- 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 "nsAutoRef.h"
+#include <cmath>
+
+/*
+ * Helper that manages the destruction of glib objects as soon as they leave
+ * the current scope.
+ *
+ * We are specializing nsAutoRef class.
+ */
+
+template <>
+class nsAutoRefTraits<GHashTable> : public nsPointerRefTraits<GHashTable>
+{
+public:
+ static void Release(GHashTable* ptr) { g_hash_table_unref(ptr); }
+};
+
+using namespace mozilla::dom::battery;
+
+namespace mozilla {
+namespace 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.
+ * Note: the caller has to unref the hash table.
+ */
+ 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.
+ DBusGConnection* mDBusConnection;
+
+ // The DBus proxy object to upower.
+ DBusGProxy* mUPowerProxy;
+
+ // The path of the tracked device.
+ gchar* mTrackedDevice;
+
+ // The DBusGProxy for the tracked device.
+ 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()
+ : mDBusConnection(nullptr)
+ , mUPowerProxy(nullptr)
+ , mTrackedDevice(nullptr)
+ , mTrackedDeviceProxy(nullptr)
+ , 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()
+{
+ GError* error = nullptr;
+ mDBusConnection = dbus_g_bus_get(DBUS_BUS_SYSTEM, &error);
+
+ if (!mDBusConnection) {
+ HAL_LOG("Failed to open connection to bus: %s\n", error->message);
+ g_error_free(error);
+ 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 = 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);
+
+ g_free(mTrackedDevice);
+ mTrackedDevice = nullptr;
+
+ if (mTrackedDeviceProxy) {
+ dbus_g_proxy_disconnect_signal(mTrackedDeviceProxy, "PropertiesChanged",
+ G_CALLBACK (PropertiesChanged), this);
+
+ g_object_unref(mTrackedDeviceProxy);
+ mTrackedDeviceProxy = nullptr;
+ }
+
+ g_object_unref(mUPowerProxy);
+ mUPowerProxy = nullptr;
+
+ dbus_g_connection_unref(mDBusConnection);
+ 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;
+ GError* error = nullptr;
+
+ // Reset the current tracked device:
+ g_free(mTrackedDevice);
+ mTrackedDevice = nullptr;
+
+ // Reset the current tracked device proxy:
+ if (mTrackedDeviceProxy) {
+ dbus_g_proxy_disconnect_signal(mTrackedDeviceProxy, "PropertiesChanged",
+ G_CALLBACK (PropertiesChanged), this);
+
+ g_object_unref(mTrackedDeviceProxy);
+ mTrackedDeviceProxy = nullptr;
+ }
+
+ // If that fails, that likely means upower isn't installed.
+ if (!dbus_g_proxy_call(mUPowerProxy, "EnumerateDevices", &error, G_TYPE_INVALID,
+ typeGPtrArray, &devices, G_TYPE_INVALID)) {
+ HAL_LOG("Error: %s\n", error->message);
+ g_error_free(error);
+ 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) {
+ gchar* devicePath = static_cast<gchar*>(g_ptr_array_index(devices, i));
+
+ DBusGProxy* proxy = dbus_g_proxy_new_from_proxy(mUPowerProxy,
+ "org.freedesktop.DBus.Properties",
+ devicePath);
+
+ nsAutoRef<GHashTable> hashTable(GetDevicePropertiesSync(proxy));
+
+ if (g_value_get_uint(static_cast<const GValue*>(g_hash_table_lookup(hashTable, "Type"))) == sDeviceTypeBattery) {
+ UpdateSavedInfo(hashTable);
+ mTrackedDevice = devicePath;
+ mTrackedDeviceProxy = proxy;
+ break;
+ }
+
+ g_object_unref(proxy);
+ g_free(devicePath);
+ }
+
+ 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)) {
+#else
+ if (g_ascii_strcasecmp(aObjectPath, aListener->mTrackedDevice)) {
+#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;
+}
+
+GHashTable*
+UPowerClient::GetDevicePropertiesSync(DBusGProxy* aProxy)
+{
+ GError* error = nullptr;
+ GHashTable* hashTable = nullptr;
+ GType typeGHashTable = dbus_g_type_get_map("GHashTable", G_TYPE_STRING,
+ G_TYPE_VALUE);
+ if (!dbus_g_proxy_call(aProxy, "GetAll", &error, G_TYPE_STRING,
+ "org.freedesktop.UPower.Device", G_TYPE_INVALID,
+ typeGHashTable, &hashTable, G_TYPE_INVALID)) {
+ HAL_LOG("Error: %s\n", error->message);
+ g_error_free(error);
+ return nullptr;
+ }
+
+ return hashTable;
+}
+
+/* static */ void
+UPowerClient::GetDevicePropertiesCallback(DBusGProxy* aProxy,
+ DBusGProxyCall* aCall, void* aData)
+{
+ GError* error = nullptr;
+ GHashTable* hashTable = nullptr;
+ GType typeGHashTable = dbus_g_type_get_map("GHashTable", G_TYPE_STRING,
+ G_TYPE_VALUE);
+ if (!dbus_g_proxy_end_call(aProxy, aCall, &error, typeGHashTable,
+ &hashTable, G_TYPE_INVALID)) {
+ HAL_LOG("Error: %s\n", error->message);
+ g_error_free(error);
+ } 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;
+ MOZ_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 hal_impl
+} // namespace mozilla
diff --git a/hal/moz.build b/hal/moz.build
new file mode 100644
index 000000000..c17379f22
--- /dev/null
+++ b/hal/moz.build
@@ -0,0 +1,185 @@
+# -*- 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/.
+
+XPIDL_SOURCES += [
+ 'gonk/nsIRecoveryService.idl',
+]
+
+XPIDL_MODULE = 'hal'
+
+EXPORTS.mozilla += [
+ 'Hal.h',
+ 'HalImpl.h',
+ 'HalSandbox.h',
+ 'HalScreenConfiguration.h',
+ 'HalSensor.h',
+ 'HalTypes.h',
+ 'HalWakeLock.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/AndroidAlarm.cpp',
+ 'android/AndroidSensor.cpp',
+ 'fallback/FallbackPower.cpp',
+ 'linux/LinuxMemory.cpp',
+ ]
+ # AndroidHal.cpp cannot be built in unified mode because it relies on HalImpl.h.
+ SOURCES += [
+ 'android/AndroidHal.cpp',
+ ]
+elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk':
+ UNIFIED_SOURCES += [
+ 'gonk/GonkDiskSpaceWatcher.cpp',
+ 'gonk/GonkSensor.cpp',
+ 'gonk/GonkSensorsHelpers.cpp',
+ 'gonk/GonkSensorsInterface.cpp',
+ 'gonk/GonkSensorsPollInterface.cpp',
+ 'gonk/GonkSensorsRegistryInterface.cpp',
+ 'gonk/GonkSwitch.cpp',
+ 'gonk/SystemService.cpp',
+ 'gonk/UeventPoller.cpp',
+ 'linux/LinuxMemory.cpp',
+ 'linux/LinuxPower.cpp',
+ ]
+ # GonkHal.cpp cannot be built in unified mode because it relies on HalImpl.h.
+ SOURCES += [
+ 'gonk/GonkHal.cpp',
+ ]
+elif CONFIG['OS_TARGET'] == 'Linux':
+ UNIFIED_SOURCES += [
+ 'fallback/FallbackAlarm.cpp',
+ 'fallback/FallbackScreenConfiguration.cpp',
+ 'fallback/FallbackSensor.cpp',
+ 'fallback/FallbackVibration.cpp',
+ 'linux/LinuxMemory.cpp',
+ 'linux/LinuxPower.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/FallbackAlarm.cpp',
+ 'fallback/FallbackMemory.cpp',
+ 'fallback/FallbackPower.cpp',
+ 'fallback/FallbackScreenConfiguration.cpp',
+ 'fallback/FallbackVibration.cpp',
+ 'windows/WindowsSensor.cpp',
+ ]
+ # WindowsBattery.cpp cannot be built in unified mode because it relies on HalImpl.h.
+ SOURCES += [
+ 'windows/WindowsBattery.cpp',
+ ]
+elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
+ UNIFIED_SOURCES += [
+ 'cocoa/CocoaBattery.cpp',
+ 'fallback/FallbackAlarm.cpp',
+ 'fallback/FallbackMemory.cpp',
+ 'fallback/FallbackPower.cpp',
+ 'fallback/FallbackScreenConfiguration.cpp',
+ 'fallback/FallbackVibration.cpp',
+ ]
+elif CONFIG['OS_TARGET'] in ('OpenBSD', 'NetBSD', 'FreeBSD', 'DragonFly'):
+ UNIFIED_SOURCES += [
+ 'fallback/FallbackAlarm.cpp',
+ 'fallback/FallbackMemory.cpp',
+ 'fallback/FallbackPower.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/FallbackAlarm.cpp',
+ 'fallback/FallbackBattery.cpp',
+ 'fallback/FallbackMemory.cpp',
+ 'fallback/FallbackPower.cpp',
+ 'fallback/FallbackScreenConfiguration.cpp',
+ 'fallback/FallbackSensor.cpp',
+ 'fallback/FallbackVibration.cpp',
+ ]
+
+# Fallbacks for backends implemented on Gonk only.
+if CONFIG['MOZ_WIDGET_TOOLKIT'] != 'gonk':
+ UNIFIED_SOURCES += [
+ 'fallback/FallbackDiskSpaceWatcher.cpp',
+ 'fallback/FallbackFactoryReset.cpp',
+ 'fallback/FallbackProcessPriority.cpp',
+ 'fallback/FallbackScreenPower.cpp',
+ 'fallback/FallbackSwitch.cpp',
+ 'fallback/FallbackSystemService.cpp',
+ 'fallback/FallbackThreadPriority.cpp',
+ 'fallback/FallbackTime.cpp',
+ 'fallback/FallbackWakeLocks.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'
+
+if CONFIG['MOZ_GAMEPAD']:
+ LOCAL_INCLUDES += [
+ '/dom/base',
+ ]
+
+if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk':
+ # So that we can call nsScreenManagerGonk::GetConfiguration().
+ LOCAL_INCLUDES += [
+ '/widget',
+ '/widget/gonk',
+ ]
+
+if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk':
+ LOCAL_INCLUDES += ['%' + '%s/hardware/libhardware_legacy/include' % CONFIG['ANDROID_SOURCE']]
+
+CFLAGS += CONFIG['GLIB_CFLAGS']
+CFLAGS += CONFIG['MOZ_DBUS_GLIB_CFLAGS']
+CXXFLAGS += CONFIG['GLIB_CFLAGS']
+CXXFLAGS += CONFIG['MOZ_DBUS_GLIB_CFLAGS']
diff --git a/hal/sandbox/PHal.ipdl b/hal/sandbox/PHal.ipdl
new file mode 100644
index 000000000..37b69f93f
--- /dev/null
+++ b/hal/sandbox/PHal.ipdl
@@ -0,0 +1,159 @@
+/* -*- 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/GfxMessageUtils.h";
+
+using mozilla::dom::ScreenOrientationInternal from "mozilla/dom/ScreenOrientation.h";
+using mozilla::hal::SensorType from "mozilla/HalSensor.h";
+using mozilla::hal::SensorAccuracyType from "mozilla/HalSensor.h";
+using mozilla::hal::WakeLockControl from "mozilla/HalTypes.h";
+using mozilla::hal::SwitchState from "mozilla/HalTypes.h";
+using mozilla::hal::SwitchDevice 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;
+ SensorAccuracyType accuracy;
+};
+
+struct NetworkInformation {
+ uint32_t type;
+ bool isWifi;
+ uint32_t dhcpGateway;
+};
+
+struct SwitchEvent {
+ SwitchDevice device;
+ SwitchState status;
+};
+
+struct WakeLockInformation {
+ nsString topic;
+ uint32_t numLocks;
+ uint32_t numHidden;
+ uint64_t[] lockingProcesses;
+};
+
+struct ScreenConfiguration {
+ nsIntRect rect;
+ ScreenOrientationInternal orientation;
+ uint16_t angle;
+ uint32_t colorDepth;
+ uint32_t pixelDepth;
+};
+
+struct SystemTimezoneChangeInformation {
+ // These timezone offsets are relative to UTC in minutes and
+ // have already taken daylight saving time (DST) into account.
+ int32_t oldTimezoneOffsetMinutes;
+ int32_t newTimezoneOffsetMinutes;
+};
+
+} // namespace hal
+
+namespace hal_sandbox {
+
+nested(upto inside_cpow) sync protocol PHal {
+ manager PContent;
+
+child:
+ async NotifyBatteryChange(BatteryInformation aBatteryInfo);
+ async NotifyNetworkChange(NetworkInformation aNetworkInfo);
+ async NotifyWakeLockChange(WakeLockInformation aWakeLockInfo);
+ async NotifyScreenConfigurationChange(ScreenConfiguration aScreenOrientation);
+ async NotifySwitchChange(SwitchEvent aEvent);
+ async NotifySystemClockChange(int64_t aClockDeltaMS);
+ async NotifySystemTimezoneChange(SystemTimezoneChangeInformation aSystemTimezoneChangeInfo);
+
+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);
+
+ sync GetScreenEnabled() returns (bool enabled);
+ async SetScreenEnabled(bool aEnabled);
+
+ sync GetKeyLightEnabled() returns (bool enabled);
+ async SetKeyLightEnabled(bool aEnabled);
+
+ sync GetCpuSleepAllowed() returns (bool allowed);
+ async SetCpuSleepAllowed(bool aAllowed);
+
+ sync GetScreenBrightness() returns (double brightness);
+ async SetScreenBrightness(double aBrightness);
+
+ async AdjustSystemClock(int64_t aDeltaMilliseconds);
+ async SetTimezone(nsCString aTimezoneSpec);
+ sync GetTimezone()
+ returns (nsCString aTimezoneSpec);
+ sync GetTimezoneOffset()
+ returns (int32_t aTimezoneOffset);
+ async EnableSystemClockChangeNotifications();
+ async DisableSystemClockChangeNotifications();
+ async EnableSystemTimezoneChangeNotifications();
+ async DisableSystemTimezoneChangeNotifications();
+
+ async ModifyWakeLock(nsString aTopic,
+ WakeLockControl aLockAdjust,
+ WakeLockControl aHiddenAdjust,
+ uint64_t aProcessID);
+ async EnableWakeLockNotifications();
+ async DisableWakeLockNotifications();
+ sync GetWakeLockInfo(nsString aTopic)
+ returns (WakeLockInformation aWakeLockInfo);
+
+ async EnableScreenConfigurationNotifications();
+ async DisableScreenConfigurationNotifications();
+ nested(inside_cpow) sync GetCurrentScreenConfiguration()
+ returns (ScreenConfiguration aScreenConfiguration);
+ sync LockScreenOrientation(ScreenOrientationInternal aOrientation)
+ returns (bool allowed);
+ async UnlockScreenOrientation();
+
+ async EnableSwitchNotifications(SwitchDevice aDevice);
+ async DisableSwitchNotifications(SwitchDevice aDevice);
+ sync GetCurrentSwitchState(SwitchDevice aDevice)
+ returns (SwitchState aState);
+
+ async FactoryReset(nsString aReason);
+
+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 000000000..d33a53a2f
--- /dev/null
+++ b/hal/sandbox/SandboxHal.cpp
@@ -0,0 +1,907 @@
+/* -*- 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/AppProcessChecker.h"
+#include "mozilla/dom/ContentChild.h"
+#include "mozilla/dom/ContentParent.h"
+#include "mozilla/hal_sandbox/PHalChild.h"
+#include "mozilla/hal_sandbox/PHalParent.h"
+#include "mozilla/dom/TabParent.h"
+#include "mozilla/dom/TabChild.h"
+#include "mozilla/dom/battery/Types.h"
+#include "mozilla/dom/network/Types.h"
+#include "mozilla/dom/ScreenOrientation.h"
+#include "mozilla/Observer.h"
+#include "mozilla/Unused.h"
+#include "nsAutoPtr.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, const WindowIdentifier &id)
+{
+ HAL_LOG("Vibrate: Sending to parent process.");
+
+ AutoTArray<uint32_t, 8> p(pattern);
+
+ WindowIdentifier newID(id);
+ newID.AppendProcessID();
+ Hal()->SendVibrate(p, newID.AsArray(), TabChild::GetFrom(newID.GetWindow()));
+}
+
+void
+CancelVibrate(const WindowIdentifier &id)
+{
+ HAL_LOG("CancelVibrate: Sending to parent process.");
+
+ WindowIdentifier newID(id);
+ newID.AppendProcessID();
+ Hal()->SendCancelVibrate(newID.AsArray(), TabChild::GetFrom(newID.GetWindow()));
+}
+
+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);
+}
+
+void
+EnableScreenConfigurationNotifications()
+{
+ Hal()->SendEnableScreenConfigurationNotifications();
+}
+
+void
+DisableScreenConfigurationNotifications()
+{
+ Hal()->SendDisableScreenConfigurationNotifications();
+}
+
+void
+GetCurrentScreenConfiguration(ScreenConfiguration* aScreenConfiguration)
+{
+ Hal()->SendGetCurrentScreenConfiguration(aScreenConfiguration);
+}
+
+bool
+LockScreenOrientation(const dom::ScreenOrientationInternal& aOrientation)
+{
+ bool allowed;
+ Hal()->SendLockScreenOrientation(aOrientation, &allowed);
+ return allowed;
+}
+
+void
+UnlockScreenOrientation()
+{
+ Hal()->SendUnlockScreenOrientation();
+}
+
+bool
+GetScreenEnabled()
+{
+ bool enabled = false;
+ Hal()->SendGetScreenEnabled(&enabled);
+ return enabled;
+}
+
+void
+SetScreenEnabled(bool aEnabled)
+{
+ Hal()->SendSetScreenEnabled(aEnabled);
+}
+
+bool
+GetKeyLightEnabled()
+{
+ bool enabled = false;
+ Hal()->SendGetKeyLightEnabled(&enabled);
+ return enabled;
+}
+
+void
+SetKeyLightEnabled(bool aEnabled)
+{
+ Hal()->SendSetKeyLightEnabled(aEnabled);
+}
+
+bool
+GetCpuSleepAllowed()
+{
+ bool allowed = true;
+ Hal()->SendGetCpuSleepAllowed(&allowed);
+ return allowed;
+}
+
+void
+SetCpuSleepAllowed(bool aAllowed)
+{
+ Hal()->SendSetCpuSleepAllowed(aAllowed);
+}
+
+double
+GetScreenBrightness()
+{
+ double brightness = 0;
+ Hal()->SendGetScreenBrightness(&brightness);
+ return brightness;
+}
+
+void
+SetScreenBrightness(double aBrightness)
+{
+ Hal()->SendSetScreenBrightness(aBrightness);
+}
+
+void
+AdjustSystemClock(int64_t aDeltaMilliseconds)
+{
+ Hal()->SendAdjustSystemClock(aDeltaMilliseconds);
+}
+
+void
+SetTimezone(const nsCString& aTimezoneSpec)
+{
+ Hal()->SendSetTimezone(nsCString(aTimezoneSpec));
+}
+
+nsCString
+GetTimezone()
+{
+ nsCString timezone;
+ Hal()->SendGetTimezone(&timezone);
+ return timezone;
+}
+
+int32_t
+GetTimezoneOffset()
+{
+ int32_t timezoneOffset;
+ Hal()->SendGetTimezoneOffset(&timezoneOffset);
+ return timezoneOffset;
+}
+
+void
+EnableSystemClockChangeNotifications()
+{
+ Hal()->SendEnableSystemClockChangeNotifications();
+}
+
+void
+DisableSystemClockChangeNotifications()
+{
+ Hal()->SendDisableSystemClockChangeNotifications();
+}
+
+void
+EnableSystemTimezoneChangeNotifications()
+{
+ Hal()->SendEnableSystemTimezoneChangeNotifications();
+}
+
+void
+DisableSystemTimezoneChangeNotifications()
+{
+ Hal()->SendDisableSystemTimezoneChangeNotifications();
+}
+
+void
+Reboot()
+{
+ NS_RUNTIMEABORT("Reboot() can't be called from sandboxed contexts.");
+}
+
+void
+PowerOff()
+{
+ NS_RUNTIMEABORT("PowerOff() can't be called from sandboxed contexts.");
+}
+
+void
+StartForceQuitWatchdog(ShutdownMode aMode, int32_t aTimeoutSecs)
+{
+ NS_RUNTIMEABORT("StartForceQuitWatchdog() can't be called from sandboxed contexts.");
+}
+
+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(nsString(aTopic), aLockAdjust, aHiddenAdjust, aProcessID);
+}
+
+void
+GetWakeLockInfo(const nsAString &aTopic, WakeLockInformation *aWakeLockInfo)
+{
+ Hal()->SendGetWakeLockInfo(nsString(aTopic), aWakeLockInfo);
+}
+
+void
+EnableSwitchNotifications(SwitchDevice aDevice)
+{
+ Hal()->SendEnableSwitchNotifications(aDevice);
+}
+
+void
+DisableSwitchNotifications(SwitchDevice aDevice)
+{
+ Hal()->SendDisableSwitchNotifications(aDevice);
+}
+
+SwitchState
+GetCurrentSwitchState(SwitchDevice aDevice)
+{
+ SwitchState state;
+ Hal()->SendGetCurrentSwitchState(aDevice, &state);
+ return state;
+}
+
+void
+NotifySwitchStateFromInputDevice(SwitchDevice aDevice, SwitchState aState)
+{
+ Unused << aDevice;
+ Unused << aState;
+ NS_RUNTIMEABORT("Only the main process may notify switch state change.");
+}
+
+bool
+EnableAlarm()
+{
+ NS_RUNTIMEABORT("Alarms can't be programmed from sandboxed contexts. Yet.");
+ return false;
+}
+
+void
+DisableAlarm()
+{
+ NS_RUNTIMEABORT("Alarms can't be programmed from sandboxed contexts. Yet.");
+}
+
+bool
+SetAlarm(int32_t aSeconds, int32_t aNanoseconds)
+{
+ NS_RUNTIMEABORT("Alarms can't be programmed from sandboxed contexts. Yet.");
+ return false;
+}
+
+void
+SetProcessPriority(int aPid, ProcessPriority aPriority, uint32_t aLRU)
+{
+ NS_RUNTIMEABORT("Only the main process may set processes' priorities.");
+}
+
+void
+SetCurrentThreadPriority(ThreadPriority aThreadPriority)
+{
+ NS_RUNTIMEABORT("Setting current thread priority cannot be called from sandboxed contexts.");
+}
+
+void
+SetThreadPriority(PlatformThreadId aThreadId,
+ ThreadPriority aThreadPriority)
+{
+ NS_RUNTIMEABORT("Setting thread priority cannot be called from sandboxed contexts.");
+}
+
+void
+FactoryReset(FactoryResetReason& aReason)
+{
+ if (aReason == FactoryResetReason::Normal) {
+ Hal()->SendFactoryReset(NS_LITERAL_STRING("normal"));
+ } else if (aReason == FactoryResetReason::Wipe) {
+ Hal()->SendFactoryReset(NS_LITERAL_STRING("wipe"));
+ } else if (aReason == FactoryResetReason::Root) {
+ Hal()->SendFactoryReset(NS_LITERAL_STRING("root"));
+ }
+}
+
+void
+StartDiskSpaceWatcher()
+{
+ NS_RUNTIMEABORT("StartDiskSpaceWatcher() can't be called from sandboxed contexts.");
+}
+
+void
+StopDiskSpaceWatcher()
+{
+ NS_RUNTIMEABORT("StopDiskSpaceWatcher() can't be called from sandboxed contexts.");
+}
+
+bool IsHeadphoneEventFromInputDev()
+{
+ NS_RUNTIMEABORT("IsHeadphoneEventFromInputDev() cannot be called from sandboxed contexts.");
+ return false;
+}
+
+nsresult StartSystemService(const char* aSvcName, const char* aArgs)
+{
+ NS_RUNTIMEABORT("System services cannot be controlled from sandboxed contexts.");
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+void StopSystemService(const char* aSvcName)
+{
+ NS_RUNTIMEABORT("System services cannot be controlled from sandboxed contexts.");
+}
+
+bool SystemServiceIsRunning(const char* aSvcName)
+{
+ NS_RUNTIMEABORT("System services cannot be controlled from sandboxed contexts.");
+ return false;
+}
+
+class HalParent : public PHalParent
+ , public BatteryObserver
+ , public NetworkObserver
+ , public ISensorObserver
+ , public WakeLockObserver
+ , public ScreenConfigurationObserver
+ , public SwitchObserver
+ , public SystemClockChangeObserver
+ , public SystemTimezoneChangeObserver
+{
+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);
+ hal::UnregisterScreenConfigurationObserver(this);
+ for (int32_t sensor = SENSOR_UNKNOWN + 1;
+ sensor < NUM_SENSOR_TYPE; ++sensor) {
+ hal::UnregisterSensorObserver(SensorType(sensor), this);
+ }
+ hal::UnregisterWakeLockObserver(this);
+ hal::UnregisterSystemClockChangeObserver(this);
+ hal::UnregisterSystemTimezoneChangeObserver(this);
+ for (int32_t switchDevice = SWITCH_DEVICE_UNKNOWN + 1;
+ switchDevice < NUM_SWITCH_DEVICE; ++switchDevice) {
+ hal::UnregisterSwitchObserver(SwitchDevice(switchDevice), this);
+ }
+ }
+
+ virtual bool
+ RecvVibrate(InfallibleTArray<unsigned int>&& pattern,
+ InfallibleTArray<uint64_t>&& id,
+ PBrowserParent *browserParent) override
+ {
+ // We give all content vibration permission.
+ // TabParent *tabParent = TabParent::GetFrom(browserParent);
+ /* xxxkhuey wtf
+ nsCOMPtr<nsIDOMWindow> window =
+ do_QueryInterface(tabParent->GetBrowserDOMWindow());
+ */
+ WindowIdentifier newID(id, nullptr);
+ hal::Vibrate(pattern, newID);
+ return true;
+ }
+
+ virtual bool
+ RecvCancelVibrate(InfallibleTArray<uint64_t> &&id,
+ PBrowserParent *browserParent) override
+ {
+ //TabParent *tabParent = TabParent::GetFrom(browserParent);
+ /* XXXkhuey wtf
+ nsCOMPtr<nsIDOMWindow> window =
+ tabParent->GetBrowserDOMWindow();
+ */
+ WindowIdentifier newID(id, nullptr);
+ hal::CancelVibrate(newID);
+ return true;
+ }
+
+ virtual bool
+ RecvEnableBatteryNotifications() override {
+ // We give all content battery-status permission.
+ hal::RegisterBatteryObserver(this);
+ return true;
+ }
+
+ virtual bool
+ RecvDisableBatteryNotifications() override {
+ hal::UnregisterBatteryObserver(this);
+ return true;
+ }
+
+ virtual bool
+ RecvGetCurrentBatteryInformation(BatteryInformation* aBatteryInfo) override {
+ // We give all content battery-status permission.
+ hal::GetCurrentBatteryInformation(aBatteryInfo);
+ return true;
+ }
+
+ void Notify(const BatteryInformation& aBatteryInfo) override {
+ Unused << SendNotifyBatteryChange(aBatteryInfo);
+ }
+
+ virtual bool
+ RecvEnableNetworkNotifications() override {
+ // We give all content access to this network-status information.
+ hal::RegisterNetworkObserver(this);
+ return true;
+ }
+
+ virtual bool
+ RecvDisableNetworkNotifications() override {
+ hal::UnregisterNetworkObserver(this);
+ return true;
+ }
+
+ virtual bool
+ RecvGetCurrentNetworkInformation(NetworkInformation* aNetworkInfo) override {
+ hal::GetCurrentNetworkInformation(aNetworkInfo);
+ return true;
+ }
+
+ void Notify(const NetworkInformation& aNetworkInfo) override {
+ Unused << SendNotifyNetworkChange(aNetworkInfo);
+ }
+
+ virtual bool
+ RecvEnableScreenConfigurationNotifications() override {
+ // Screen configuration is used to implement CSS and DOM
+ // properties, so all content already has access to this.
+ hal::RegisterScreenConfigurationObserver(this);
+ return true;
+ }
+
+ virtual bool
+ RecvDisableScreenConfigurationNotifications() override {
+ hal::UnregisterScreenConfigurationObserver(this);
+ return true;
+ }
+
+ virtual bool
+ RecvGetCurrentScreenConfiguration(ScreenConfiguration* aScreenConfiguration) override {
+ hal::GetCurrentScreenConfiguration(aScreenConfiguration);
+ return true;
+ }
+
+ virtual bool
+ RecvLockScreenOrientation(const dom::ScreenOrientationInternal& aOrientation, bool* aAllowed) 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.
+ *aAllowed = hal::LockScreenOrientation(aOrientation);
+ return true;
+ }
+
+ virtual bool
+ RecvUnlockScreenOrientation() override
+ {
+ hal::UnlockScreenOrientation();
+ return true;
+ }
+
+ void Notify(const ScreenConfiguration& aScreenConfiguration) override {
+ Unused << SendNotifyScreenConfigurationChange(aScreenConfiguration);
+ }
+
+ virtual bool
+ RecvGetScreenEnabled(bool* aEnabled) override
+ {
+ if (!AssertAppProcessPermission(this, "power")) {
+ return false;
+ }
+ *aEnabled = hal::GetScreenEnabled();
+ return true;
+ }
+
+ virtual bool
+ RecvSetScreenEnabled(const bool& aEnabled) override
+ {
+ if (!AssertAppProcessPermission(this, "power")) {
+ return false;
+ }
+ hal::SetScreenEnabled(aEnabled);
+ return true;
+ }
+
+ virtual bool
+ RecvGetKeyLightEnabled(bool* aEnabled) override
+ {
+ if (!AssertAppProcessPermission(this, "power")) {
+ return false;
+ }
+ *aEnabled = hal::GetKeyLightEnabled();
+ return true;
+ }
+
+ virtual bool
+ RecvSetKeyLightEnabled(const bool& aEnabled) override
+ {
+ if (!AssertAppProcessPermission(this, "power")) {
+ return false;
+ }
+ hal::SetKeyLightEnabled(aEnabled);
+ return true;
+ }
+
+ virtual bool
+ RecvGetCpuSleepAllowed(bool* aAllowed) override
+ {
+ if (!AssertAppProcessPermission(this, "power")) {
+ return false;
+ }
+ *aAllowed = hal::GetCpuSleepAllowed();
+ return true;
+ }
+
+ virtual bool
+ RecvSetCpuSleepAllowed(const bool& aAllowed) override
+ {
+ if (!AssertAppProcessPermission(this, "power")) {
+ return false;
+ }
+ hal::SetCpuSleepAllowed(aAllowed);
+ return true;
+ }
+
+ virtual bool
+ RecvGetScreenBrightness(double* aBrightness) override
+ {
+ if (!AssertAppProcessPermission(this, "power")) {
+ return false;
+ }
+ *aBrightness = hal::GetScreenBrightness();
+ return true;
+ }
+
+ virtual bool
+ RecvSetScreenBrightness(const double& aBrightness) override
+ {
+ if (!AssertAppProcessPermission(this, "power")) {
+ return false;
+ }
+ hal::SetScreenBrightness(aBrightness);
+ return true;
+ }
+
+ virtual bool
+ RecvAdjustSystemClock(const int64_t &aDeltaMilliseconds) override
+ {
+ if (!AssertAppProcessPermission(this, "time")) {
+ return false;
+ }
+ hal::AdjustSystemClock(aDeltaMilliseconds);
+ return true;
+ }
+
+ virtual bool
+ RecvSetTimezone(const nsCString& aTimezoneSpec) override
+ {
+ if (!AssertAppProcessPermission(this, "time")) {
+ return false;
+ }
+ hal::SetTimezone(aTimezoneSpec);
+ return true;
+ }
+
+ virtual bool
+ RecvGetTimezone(nsCString *aTimezoneSpec) override
+ {
+ if (!AssertAppProcessPermission(this, "time")) {
+ return false;
+ }
+ *aTimezoneSpec = hal::GetTimezone();
+ return true;
+ }
+
+ virtual bool
+ RecvGetTimezoneOffset(int32_t *aTimezoneOffset) override
+ {
+ if (!AssertAppProcessPermission(this, "time")) {
+ return false;
+ }
+ *aTimezoneOffset = hal::GetTimezoneOffset();
+ return true;
+ }
+
+ virtual bool
+ RecvEnableSystemClockChangeNotifications() override
+ {
+ hal::RegisterSystemClockChangeObserver(this);
+ return true;
+ }
+
+ virtual bool
+ RecvDisableSystemClockChangeNotifications() override
+ {
+ hal::UnregisterSystemClockChangeObserver(this);
+ return true;
+ }
+
+ virtual bool
+ RecvEnableSystemTimezoneChangeNotifications() override
+ {
+ hal::RegisterSystemTimezoneChangeObserver(this);
+ return true;
+ }
+
+ virtual bool
+ RecvDisableSystemTimezoneChangeNotifications() override
+ {
+ hal::UnregisterSystemTimezoneChangeObserver(this);
+ return true;
+ }
+
+ virtual bool
+ RecvEnableSensorNotifications(const SensorType &aSensor) override {
+ // We currently allow any content to register device-sensor
+ // listeners.
+ hal::RegisterSensorObserver(aSensor, this);
+ return true;
+ }
+
+ virtual bool
+ RecvDisableSensorNotifications(const SensorType &aSensor) override {
+ hal::UnregisterSensorObserver(aSensor, this);
+ return true;
+ }
+
+ void Notify(const SensorData& aSensorData) override {
+ Unused << SendNotifySensorChange(aSensorData);
+ }
+
+ virtual bool
+ RecvModifyWakeLock(const nsString& 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 true;
+ }
+
+ virtual bool
+ RecvEnableWakeLockNotifications() override
+ {
+ // We allow arbitrary content to use wake locks.
+ hal::RegisterWakeLockObserver(this);
+ return true;
+ }
+
+ virtual bool
+ RecvDisableWakeLockNotifications() override
+ {
+ hal::UnregisterWakeLockObserver(this);
+ return true;
+ }
+
+ virtual bool
+ RecvGetWakeLockInfo(const nsString &aTopic, WakeLockInformation *aWakeLockInfo) override
+ {
+ hal::GetWakeLockInfo(aTopic, aWakeLockInfo);
+ return true;
+ }
+
+ void Notify(const WakeLockInformation& aWakeLockInfo) override
+ {
+ Unused << SendNotifyWakeLockChange(aWakeLockInfo);
+ }
+
+ virtual bool
+ RecvEnableSwitchNotifications(const SwitchDevice& aDevice) override
+ {
+ // Content has no reason to listen to switch events currently.
+ hal::RegisterSwitchObserver(aDevice, this);
+ return true;
+ }
+
+ virtual bool
+ RecvDisableSwitchNotifications(const SwitchDevice& aDevice) override
+ {
+ hal::UnregisterSwitchObserver(aDevice, this);
+ return true;
+ }
+
+ void Notify(const SwitchEvent& aSwitchEvent) override
+ {
+ Unused << SendNotifySwitchChange(aSwitchEvent);
+ }
+
+ virtual bool
+ RecvGetCurrentSwitchState(const SwitchDevice& aDevice, hal::SwitchState *aState) override
+ {
+ // Content has no reason to listen to switch events currently.
+ *aState = hal::GetCurrentSwitchState(aDevice);
+ return true;
+ }
+
+ void Notify(const int64_t& aClockDeltaMS) override
+ {
+ Unused << SendNotifySystemClockChange(aClockDeltaMS);
+ }
+
+ void Notify(const SystemTimezoneChangeInformation& aSystemTimezoneChangeInfo) override
+ {
+ Unused << SendNotifySystemTimezoneChange(aSystemTimezoneChangeInfo);
+ }
+
+ virtual bool
+ RecvFactoryReset(const nsString& aReason) override
+ {
+ if (!AssertAppProcessPermission(this, "power")) {
+ return false;
+ }
+
+ FactoryResetReason reason = FactoryResetReason::Normal;
+ if (aReason.EqualsLiteral("normal")) {
+ reason = FactoryResetReason::Normal;
+ } else if (aReason.EqualsLiteral("wipe")) {
+ reason = FactoryResetReason::Wipe;
+ } else if (aReason.EqualsLiteral("root")) {
+ reason = FactoryResetReason::Root;
+ } else {
+ // Invalid factory reset reason. That should never happen.
+ return false;
+ }
+
+ hal::FactoryReset(reason);
+ return true;
+ }
+};
+
+class HalChild : public PHalChild {
+public:
+ virtual void
+ ActorDestroy(ActorDestroyReason aWhy) override
+ {
+ sHalChildDestroyed = true;
+ }
+
+ virtual bool
+ RecvNotifyBatteryChange(const BatteryInformation& aBatteryInfo) override {
+ hal::NotifyBatteryChange(aBatteryInfo);
+ return true;
+ }
+
+ virtual bool
+ RecvNotifySensorChange(const hal::SensorData &aSensorData) override;
+
+ virtual bool
+ RecvNotifyNetworkChange(const NetworkInformation& aNetworkInfo) override {
+ hal::NotifyNetworkChange(aNetworkInfo);
+ return true;
+ }
+
+ virtual bool
+ RecvNotifyWakeLockChange(const WakeLockInformation& aWakeLockInfo) override {
+ hal::NotifyWakeLockChange(aWakeLockInfo);
+ return true;
+ }
+
+ virtual bool
+ RecvNotifyScreenConfigurationChange(const ScreenConfiguration& aScreenConfiguration) override {
+ hal::NotifyScreenConfigurationChange(aScreenConfiguration);
+ return true;
+ }
+
+ virtual bool
+ RecvNotifySwitchChange(const mozilla::hal::SwitchEvent& aEvent) override {
+ hal::NotifySwitchChange(aEvent);
+ return true;
+ }
+
+ virtual bool
+ RecvNotifySystemClockChange(const int64_t& aClockDeltaMS) override {
+ hal::NotifySystemClockChange(aClockDeltaMS);
+ return true;
+ }
+
+ virtual bool
+ RecvNotifySystemTimezoneChange(
+ const SystemTimezoneChangeInformation& aSystemTimezoneChangeInfo) override {
+ hal::NotifySystemTimezoneChange(aSystemTimezoneChangeInfo);
+ return true;
+ }
+};
+
+bool
+HalChild::RecvNotifySensorChange(const hal::SensorData &aSensorData) {
+ hal::NotifySensorChange(aSensorData);
+
+ return true;
+}
+
+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 000000000..0da2d41fb
--- /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 000000000..c1b9a31e6
--- /dev/null
+++ b/hal/windows/WindowsBattery.cpp
@@ -0,0 +1,190 @@
+/* -*- 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 "nsITimer.h"
+#include "mozilla/Preferences.h"
+#include "mozilla/dom/battery/Constants.h"
+#include "nsComponentManagerUtils.h"
+
+#include <windows.h>
+#include "mozilla/WindowsVersion.h"
+
+using namespace mozilla::dom::battery;
+
+namespace mozilla {
+namespace hal_impl {
+
+static nsCOMPtr<nsITimer> sUpdateTimer;
+
+/* Power Event API is Vista or later */
+static decltype(RegisterPowerSettingNotification)* sRegisterPowerSettingNotification = nullptr;
+static decltype(UnregisterPowerSettingNotification)* sUnregisterPowerSettingNotification = nullptr;
+static HPOWERNOTIFY sPowerHandle = nullptr;
+static HPOWERNOTIFY sCapacityHandle = nullptr;
+static HWND sHWnd = nullptr;
+
+static void
+UpdateHandler(nsITimer* aTimer, void* aClosure) {
+ NS_ASSERTION(!IsVistaOrLater(),
+ "We shouldn't call this function for Vista or later version!");
+
+ static hal::BatteryInformation sLastInfo;
+ hal::BatteryInformation currentInfo;
+
+ hal_impl::GetCurrentBatteryInformation(&currentInfo);
+ if (sLastInfo.level() != currentInfo.level() ||
+ sLastInfo.charging() != currentInfo.charging() ||
+ sLastInfo.remainingTime() != currentInfo.remainingTime()) {
+ hal::NotifyBatteryChange(currentInfo);
+ sLastInfo = currentInfo;
+ }
+}
+
+static
+LRESULT CALLBACK
+BatteryWindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
+ if (msg != WM_POWERBROADCAST || wParam != PBT_POWERSETTINGCHANGE) {
+ return DefWindowProc(hwnd, msg, wParam, lParam);
+ }
+
+ hal::BatteryInformation currentInfo;
+
+ // Since we need update remainingTime, we cannot use LPARAM.
+ hal_impl::GetCurrentBatteryInformation(&currentInfo);
+
+ hal::NotifyBatteryChange(currentInfo);
+ return TRUE;
+}
+
+void
+EnableBatteryNotifications()
+{
+ if (IsVistaOrLater()) {
+ // RegisterPowerSettingNotification is from Vista or later.
+ // Use this API if available.
+ HMODULE hUser32 = GetModuleHandleW(L"USER32.DLL");
+ if (!sRegisterPowerSettingNotification)
+ sRegisterPowerSettingNotification = (decltype(RegisterPowerSettingNotification)*)
+ GetProcAddress(hUser32, "RegisterPowerSettingNotification");
+ if (!sUnregisterPowerSettingNotification)
+ sUnregisterPowerSettingNotification = (decltype(UnregisterPowerSettingNotification)*)
+ GetProcAddress(hUser32, "UnregisterPowerSettingNotification");
+
+ if (!sRegisterPowerSettingNotification ||
+ !sUnregisterPowerSettingNotification) {
+ NS_ASSERTION(false, "Canot find PowerSettingNotification functions.");
+ return;
+ }
+
+ // 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 =
+ sRegisterPowerSettingNotification(sHWnd,
+ &GUID_ACDC_POWER_SOURCE,
+ DEVICE_NOTIFY_WINDOW_HANDLE);
+ sCapacityHandle =
+ sRegisterPowerSettingNotification(sHWnd,
+ &GUID_BATTERY_PERCENTAGE_REMAINING,
+ DEVICE_NOTIFY_WINDOW_HANDLE);
+ } else
+ {
+ // for Windows XP. If we remove Windows XP support,
+ // we should remove timer-based power notification
+ sUpdateTimer = do_CreateInstance(NS_TIMER_CONTRACTID);
+ if (sUpdateTimer) {
+ sUpdateTimer->InitWithFuncCallback(UpdateHandler,
+ nullptr,
+ Preferences::GetInt("dom.battery.timer",
+ 30000 /* 30s */),
+ nsITimer::TYPE_REPEATING_SLACK);
+ }
+ }
+}
+
+void
+DisableBatteryNotifications()
+{
+ if (IsVistaOrLater()) {
+ if (sPowerHandle) {
+ sUnregisterPowerSettingNotification(sPowerHandle);
+ sPowerHandle = nullptr;
+ }
+
+ if (sCapacityHandle) {
+ sUnregisterPowerSettingNotification(sCapacityHandle);
+ sCapacityHandle = nullptr;
+ }
+
+ if (sHWnd) {
+ DestroyWindow(sHWnd);
+ sHWnd = nullptr;
+ }
+ } else
+ {
+ if (sUpdateTimer) {
+ sUpdateTimer->Cancel();
+ sUpdateTimer = 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;
+ }
+}
+
+} // hal_impl
+} // mozilla
diff --git a/hal/windows/WindowsSensor.cpp b/hal/windows/WindowsSensor.cpp
new file mode 100644
index 000000000..302ad7c48
--- /dev/null
+++ b/hal/windows/WindowsSensor.cpp
@@ -0,0 +1,184 @@
+/* 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;
+ InfallibleTArray<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::SENSOR_ACCURACY_UNKNOWN);
+ 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;
+ }
+}
+
+} // hal_impl
+} // mozilla