Bug 749551 - Alarm API (Hal/Gonk). r=cjones

This commit is contained in:
Gene Lian 2012-07-03 11:24:13 +02:00
parent 3efb0e6aa2
commit cad479cbe7
7 changed files with 301 additions and 1 deletions

View File

@ -689,5 +689,42 @@ NotifySwitchChange(const hal::SwitchEvent& aEvent)
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

View File

@ -42,6 +42,7 @@ class Observer;
namespace hal {
typedef Observer<void_t> AlarmObserver;
typedef Observer<ScreenConfiguration> ScreenConfigurationObserver;
class WindowIdentifier;
@ -366,6 +367,41 @@ void NotifySwitchChange(const hal::SwitchEvent& aEvent);
*/
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

View File

@ -64,6 +64,16 @@ void EnableSwitchNotifications(hal::SwitchDevice aDevice);
*/
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

View File

@ -47,6 +47,7 @@ CPPSRCS += \
AndroidHal.cpp \
AndroidSensor.cpp \
FallbackPower.cpp \
FallbackAlarm.cpp \
$(NULL)
else ifeq (gonk,$(MOZ_WIDGET_TOOLKIT))
CPPSRCS += \
@ -62,6 +63,7 @@ CPPSRCS += \
FallbackScreenConfiguration.cpp \
FallbackSensor.cpp \
FallbackVibration.cpp \
FallbackAlarm.cpp \
$(NULL)
ifdef MOZ_ENABLE_DBUS
CPPSRCS += UPowerClient.cpp
@ -75,6 +77,7 @@ CPPSRCS += \
FallbackVibration.cpp \
FallbackScreenConfiguration.cpp \
FallbackPower.cpp \
FallbackAlarm.cpp \
$(NULL)
else ifeq (cocoa,$(MOZ_WIDGET_TOOLKIT))
CPPSRCS += \
@ -82,6 +85,7 @@ CPPSRCS += \
FallbackVibration.cpp \
FallbackPower.cpp \
FallbackScreenConfiguration.cpp \
FallbackAlarm.cpp \
$(NULL)
CMMSRCS += \
CocoaSensor.mm \
@ -93,6 +97,7 @@ CPPSRCS += \
FallbackVibration.cpp \
FallbackPower.cpp \
FallbackScreenConfiguration.cpp \
FallbackAlarm.cpp \
$(NULL)
ifdef MOZ_ENABLE_DBUS
CPPSRCS += UPowerClient.cpp
@ -106,6 +111,7 @@ CPPSRCS += \
FallbackVibration.cpp \
FallbackPower.cpp \
FallbackScreenConfiguration.cpp \
FallbackAlarm.cpp \
$(NULL)
endif
@ -136,4 +142,4 @@ 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
endif

View File

@ -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

View File

@ -6,6 +6,7 @@
#include <errno.h>
#include <fcntl.h>
#include <linux/android_alarm.h>
#include <math.h>
#include <stdio.h>
#include <sys/syscall.h>
@ -626,5 +627,167 @@ 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

View File

@ -260,6 +260,26 @@ GetCurrentSwitchState(SwitchDevice aDevice)
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