mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-25 13:51:41 +00:00
Bug 749551 - Alarm API (Hal/Gonk). r=cjones
This commit is contained in:
parent
3efb0e6aa2
commit
cad479cbe7
37
hal/Hal.cpp
37
hal/Hal.cpp
@ -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
|
||||
|
36
hal/Hal.h
36
hal/Hal.h
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
28
hal/fallback/FallbackAlarm.cpp
Normal file
28
hal/fallback/FallbackAlarm.cpp
Normal 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
|
@ -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
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user