Bug 749551 - Alarm API (Hal/Gonk). r=cjones
--- a/hal/Hal.cpp
+++ b/hal/Hal.cpp
@@ -684,10 +684,47 @@ NotifySwitchChange(const hal::SwitchEven
// 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());
+}
+
+void
+UnregisterTheOneAlarmObserver()
+{
+ if (sAlarmObserver) {
+ sAlarmObserver = NULL;
+ PROXY_IF_SANDBOXED(DisableAlarm());
+ }
+}
+
+void
+NotifyAlarmFired()
+{
+ if (sAlarmObserver) {
+ sAlarmObserver->Notify(void_t());
+ }
+}
+
+bool
+SetAlarm(long aSeconds, long aNanoseconds)
+{
+ // It's pointless to program an alarm nothing is going to observe ...
+ MOZ_ASSERT(sAlarmObserver);
+ RETURN_PROXY_IF_SANDBOXED(SetAlarm(aSeconds, aNanoseconds));
+}
+
} // namespace hal
} // namespace mozilla
--- a/hal/Hal.h
+++ b/hal/Hal.h
@@ -37,16 +37,17 @@ class nsIDOMWindow;
namespace mozilla {
template <class T>
class Observer;
namespace hal {
+typedef Observer<void_t> AlarmObserver;
typedef Observer<ScreenConfiguration> ScreenConfigurationObserver;
class WindowIdentifier;
extern PRLogModuleInfo *sHalLog;
#define HAL_LOG(msg) PR_LOG(mozilla::hal::sHalLog, PR_LOG_DEBUG, msg)
} // namespace hal
@@ -361,16 +362,51 @@ void UnregisterSwitchObserver(hal::Switc
*/
void NotifySwitchChange(const hal::SwitchEvent& aEvent);
/**
* Get current switch information.
*/
hal::SwitchState GetCurrentSwitchState(hal::SwitchDevice aDevice);
+/**
+ * Register an observer that is notified when a programmed alarm
+ * expires.
+ *
+ * Currently, there can only be 0 or 1 alarm observers.
+ */
+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.
+ */
+bool SetAlarm(long aSeconds, long aNanoseconds);
+
} // namespace MOZ_HAL_NAMESPACE
} // namespace mozilla
#ifdef MOZ_DEFINED_HAL_NAMESPACE
# undef MOZ_DEFINED_HAL_NAMESPACE
# undef MOZ_HAL_NAMESPACE
#endif
--- a/hal/HalInternal.h
+++ b/hal/HalInternal.h
@@ -59,12 +59,22 @@ void DisableScreenConfigurationNotificat
*/
void EnableSwitchNotifications(hal::SwitchDevice aDevice);
/**
* Disable switch notifications from the backend
*/
void DisableSwitchNotifications(hal::SwitchDevice aDevice);
+/**
+ * Enable alarm notifications from the backend.
+ */
+bool EnableAlarm();
+
+/**
+ * Disable alarm notifications from the backend.
+ */
+void DisableAlarm();
+
} // namespace MOZ_HAL_NAMESPACE
} // namespace mozilla
#endif // mozilla_HalInternal_h
--- a/hal/Makefile.in
+++ b/hal/Makefile.in
@@ -42,75 +42,81 @@ CPPSRCS = \
HalWakeLock.cpp \
$(NULL)
ifeq (android,$(MOZ_WIDGET_TOOLKIT))
CPPSRCS += \
AndroidHal.cpp \
AndroidSensor.cpp \
FallbackPower.cpp \
+ FallbackAlarm.cpp \
$(NULL)
else ifeq (gonk,$(MOZ_WIDGET_TOOLKIT))
CPPSRCS += \
GonkHal.cpp \
LinuxPower.cpp \
GonkSensor.cpp \
UeventPoller.cpp \
GonkSwitch.cpp \
$(NULL)
else ifeq (Linux,$(OS_TARGET))
CPPSRCS += \
LinuxPower.cpp \
FallbackScreenConfiguration.cpp \
FallbackSensor.cpp \
FallbackVibration.cpp \
+ FallbackAlarm.cpp \
$(NULL)
ifdef MOZ_ENABLE_DBUS
CPPSRCS += UPowerClient.cpp
else
CPPSRCS += FallbackBattery.cpp
endif
else ifeq (WINNT,$(OS_TARGET))
CPPSRCS += \
WindowsBattery.cpp \
WindowsSensor.cpp \
FallbackVibration.cpp \
FallbackScreenConfiguration.cpp \
FallbackPower.cpp \
+ FallbackAlarm.cpp \
$(NULL)
else ifeq (cocoa,$(MOZ_WIDGET_TOOLKIT))
CPPSRCS += \
FallbackBattery.cpp \
FallbackVibration.cpp \
FallbackPower.cpp \
FallbackScreenConfiguration.cpp \
+ FallbackAlarm.cpp \
$(NULL)
CMMSRCS += \
CocoaSensor.mm \
smslib.mm \
$(NULL)
else ifneq (,$(filter OpenBSD NetBSD FreeBSD DragonFlyBSD,$(OS_TARGET)))
CPPSRCS += \
FallbackSensor.cpp \
FallbackVibration.cpp \
FallbackPower.cpp \
FallbackScreenConfiguration.cpp \
+ FallbackAlarm.cpp \
$(NULL)
ifdef MOZ_ENABLE_DBUS
CPPSRCS += UPowerClient.cpp
else
CPPSRCS += FallbackBattery.cpp
endif
else
CPPSRCS += \
FallbackBattery.cpp \
FallbackSensor.cpp \
FallbackVibration.cpp \
FallbackPower.cpp \
FallbackScreenConfiguration.cpp \
+ FallbackAlarm.cpp \
$(NULL)
endif
# Fallbacks for backends implemented on Gonk only.
ifneq (gonk,$(MOZ_WIDGET_TOOLKIT)) #{
CPPSRCS += \
FallbackLights.cpp \
FallbackTime.cpp \
@@ -131,9 +137,9 @@ include $(topsrcdir)/config/rules.mk
CFLAGS += $(MOZ_DBUS_GLIB_CFLAGS)
CXXFLAGS += $(MOZ_DBUS_GLIB_CFLAGS)
ifeq (gonk,$(MOZ_WIDGET_TOOLKIT))
# So that we can call nsScreenManagerGonk::GetConfiguration().
LOCAL_INCLUDES += -I$(topsrcdir)/widget/gonk
LOCAL_INCLUDES += -I$(topsrcdir)/widget/xpwidgets
-endif
\ No newline at end of file
+endif
new file mode 100644
--- /dev/null
+++ b/hal/fallback/FallbackAlarm.cpp
@@ -0,0 +1,28 @@
+/* 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
+EnableAlarm()
+{
+ return false;
+}
+
+void
+DisableAlarm()
+{
+}
+
+bool
+SetAlarm(long aSeconds, long aNanoseconds)
+{
+ return false;
+}
+
+} // hal_impl
+} // namespace mozilla
--- a/hal/gonk/GonkHal.cpp
+++ b/hal/gonk/GonkHal.cpp
@@ -1,16 +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/. */
#include <errno.h>
#include <fcntl.h>
+#include <linux/android_alarm.h>
#include <math.h>
#include <stdio.h>
#include <sys/syscall.h>
#include <time.h>
#include "android/log.h"
#include "cutils/properties.h"
#include "hardware/hardware.h"
@@ -621,10 +622,172 @@ LockScreenOrientation(const dom::ScreenO
}
void
UnlockScreenOrientation()
{
OrientationObserver::GetInstance()->UnlockScreenOrientation();
}
+
+static pthread_t sAlarmFireWatcherThread;
+
+// If |sAlarmData| is non-null, it's owned by the watcher thread.
+typedef struct AlarmData {
+
+public:
+ AlarmData(int aFd) : mFd(aFd), mGeneration(sNextGeneration++), mShuttingDown(false) {}
+ ScopedClose mFd;
+ int mGeneration;
+ bool mShuttingDown;
+
+ static int sNextGeneration;
+
+} AlarmData;
+
+int AlarmData::sNextGeneration = 0;
+
+AlarmData* sAlarmData = NULL;
+
+class AlarmFiredEvent : public nsRunnable {
+
+public:
+ AlarmFiredEvent(int aGeneration) : mGeneration(aGeneration) {}
+
+ NS_IMETHOD Run() {
+ // Guard against spurious notifications caused by an alarm firing
+ // concurrently with it being disabled.
+ if (sAlarmData && !sAlarmData->mShuttingDown && mGeneration == sAlarmData->mGeneration) {
+ hal::NotifyAlarmFired();
+ }
+
+ 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 == SIGUSR2) {
+ 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)) {
+ NS_DispatchToMainThread(new AlarmFiredEvent(alarmData->mGeneration));
+ }
+ }
+
+ pthread_cleanup_pop(1);
+ return NULL;
+}
+
+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;
+ }
+
+ nsAutoPtr<AlarmData> alarmData(new AlarmData(alarmFd));
+
+ struct sigaction actions;
+ memset(&actions, 0, sizeof(actions));
+ sigemptyset(&actions.sa_mask);
+ actions.sa_flags = 0;
+ actions.sa_handler = ShutDownAlarm;
+ if (sigaction(SIGUSR2, &actions, NULL)) {
+ HAL_LOG(("Failed to set SIGUSR2 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 = NULL;
+ 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.forget();
+ return true;
+}
+
+void
+DisableAlarm()
+{
+ MOZ_ASSERT(sAlarmData);
+
+ // NB: this must happen-before the thread cancellation.
+ sAlarmData = NULL;
+
+ // The cancel will interrupt the thread and destroy it, freeing the
+ // data pointed at by sAlarmData.
+ DebugOnly<int> err = pthread_kill(sAlarmFireWatcherThread, SIGUSR2);
+ MOZ_ASSERT(!err);
+}
+
+bool
+SetAlarm(long aSeconds, long 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;
+}
+
} // hal_impl
} // mozilla
--- a/hal/sandbox/SandboxHal.cpp
+++ b/hal/sandbox/SandboxHal.cpp
@@ -255,16 +255,36 @@ DisableSwitchNotifications(SwitchDevice
SwitchState
GetCurrentSwitchState(SwitchDevice aDevice)
{
SwitchState state;
Hal()->SendGetCurrentSwitchState(aDevice, &state);
return state;
}
+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(long aSeconds, long aNanoseconds)
+{
+ NS_RUNTIMEABORT("Alarms can't be programmed from sandboxed contexts. Yet.");
+ return false;
+}
+
class HalParent : public PHalParent
, public BatteryObserver
, public NetworkObserver
, public ISensorObserver
, public WakeLockObserver
, public ScreenConfigurationObserver
, public SwitchObserver
{