a1d6f92f34
On devices without /dev/alarm, use a new backend based on timerfd. timerfd has near-equivalent syscalls for the /dev/alarm ioctls we care about, with two key differences: 1) /dev/alarm uses one fd for all clocks, while timerfd needs one fd per clock type. AlarmManagerService addresses this by replacing the fd (int) with an opaque pointer (long) to the backend-specific state. 2) When the RTC changes, the /dev/alarm WAIT ioctl always returns, while timerfd cancels (and signals events) only on specially-flagged RTC timerfds. The timerfd backend masks this by creating an extraneous RTC timerfd, specifically so there's always something to signal on RTC changes. Change-Id: I5aef867748298610347f6e1479dd8bf569495832 Signed-off-by: Greg Hackmann <ghackmann@google.com>
322 lines
8.3 KiB
C++
322 lines
8.3 KiB
C++
/* //device/libs/android_runtime/android_server_AlarmManagerService.cpp
|
|
**
|
|
** Copyright 2006, The Android Open Source Project
|
|
**
|
|
** 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.
|
|
*/
|
|
|
|
#define LOG_TAG "AlarmManagerService"
|
|
|
|
#include "JNIHelp.h"
|
|
#include "jni.h"
|
|
#include <utils/Log.h>
|
|
#include <utils/misc.h>
|
|
|
|
#include <fcntl.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <sys/epoll.h>
|
|
#include <sys/timerfd.h>
|
|
#include <sys/types.h>
|
|
#include <sys/socket.h>
|
|
#include <arpa/inet.h>
|
|
#include <netinet/in.h>
|
|
#include <stdlib.h>
|
|
#include <errno.h>
|
|
#include <unistd.h>
|
|
#include <linux/ioctl.h>
|
|
#include <linux/android_alarm.h>
|
|
|
|
namespace android {
|
|
|
|
static const size_t N_ANDROID_TIMERFDS = ANDROID_ALARM_TYPE_COUNT + 1;
|
|
static const clockid_t android_alarm_to_clockid[N_ANDROID_TIMERFDS] = {
|
|
CLOCK_REALTIME_ALARM,
|
|
CLOCK_REALTIME,
|
|
CLOCK_BOOTTIME_ALARM,
|
|
CLOCK_BOOTTIME,
|
|
CLOCK_MONOTONIC,
|
|
CLOCK_REALTIME,
|
|
};
|
|
/* to match the legacy alarm driver implementation, we need an extra
|
|
CLOCK_REALTIME fd which exists specifically to be canceled on RTC changes */
|
|
|
|
class AlarmImpl
|
|
{
|
|
public:
|
|
AlarmImpl(int *fds, size_t n_fds);
|
|
virtual ~AlarmImpl();
|
|
|
|
virtual int set(int type, struct timespec *ts) = 0;
|
|
virtual int waitForAlarm() = 0;
|
|
|
|
protected:
|
|
int *fds;
|
|
size_t n_fds;
|
|
};
|
|
|
|
class AlarmImplAlarmDriver : public AlarmImpl
|
|
{
|
|
public:
|
|
AlarmImplAlarmDriver(int fd) : AlarmImpl(&fd, 1) { }
|
|
|
|
int set(int type, struct timespec *ts);
|
|
int waitForAlarm();
|
|
};
|
|
|
|
class AlarmImplTimerFd : public AlarmImpl
|
|
{
|
|
public:
|
|
AlarmImplTimerFd(int fds[N_ANDROID_TIMERFDS], int epollfd) :
|
|
AlarmImpl(fds, N_ANDROID_TIMERFDS), epollfd(epollfd) { }
|
|
~AlarmImplTimerFd();
|
|
|
|
int set(int type, struct timespec *ts);
|
|
int waitForAlarm();
|
|
|
|
private:
|
|
int epollfd;
|
|
};
|
|
|
|
AlarmImpl::AlarmImpl(int *fds_, size_t n_fds) : fds(new int[n_fds]),
|
|
n_fds(n_fds)
|
|
{
|
|
memcpy(fds, fds_, n_fds * sizeof(fds[0]));
|
|
}
|
|
|
|
AlarmImpl::~AlarmImpl()
|
|
{
|
|
for (size_t i = 0; i < n_fds; i++) {
|
|
close(fds[i]);
|
|
}
|
|
delete [] fds;
|
|
}
|
|
|
|
int AlarmImplAlarmDriver::set(int type, struct timespec *ts)
|
|
{
|
|
return ioctl(fds[0], ANDROID_ALARM_SET(type), ts);
|
|
}
|
|
|
|
int AlarmImplAlarmDriver::waitForAlarm()
|
|
{
|
|
return ioctl(fds[0], ANDROID_ALARM_WAIT);
|
|
}
|
|
|
|
AlarmImplTimerFd::~AlarmImplTimerFd()
|
|
{
|
|
for (size_t i = 0; i < N_ANDROID_TIMERFDS; i++) {
|
|
epoll_ctl(epollfd, EPOLL_CTL_DEL, fds[i], NULL);
|
|
}
|
|
close(epollfd);
|
|
}
|
|
|
|
int AlarmImplTimerFd::set(int type, struct timespec *ts)
|
|
{
|
|
if (type > ANDROID_ALARM_TYPE_COUNT) {
|
|
errno = EINVAL;
|
|
return -1;
|
|
}
|
|
|
|
if (!ts->tv_nsec && !ts->tv_sec) {
|
|
ts->tv_nsec = 1;
|
|
}
|
|
/* timerfd interprets 0 = disarm, so replace with a practically
|
|
equivalent deadline of 1 ns */
|
|
|
|
struct itimerspec spec;
|
|
memset(&spec, 0, sizeof(spec));
|
|
memcpy(&spec.it_value, ts, sizeof(spec.it_value));
|
|
|
|
return timerfd_settime(fds[type], TFD_TIMER_ABSTIME, &spec, NULL);
|
|
}
|
|
|
|
int AlarmImplTimerFd::waitForAlarm()
|
|
{
|
|
epoll_event events[N_ANDROID_TIMERFDS];
|
|
|
|
int nevents = epoll_wait(epollfd, events, N_ANDROID_TIMERFDS, -1);
|
|
if (nevents < 0) {
|
|
return nevents;
|
|
}
|
|
|
|
int result = 0;
|
|
for (int i = 0; i < nevents; i++) {
|
|
uint32_t alarm_idx = events[i].data.u32;
|
|
uint64_t unused;
|
|
ssize_t err = read(fds[alarm_idx], &unused, sizeof(unused));
|
|
if (err < 0) {
|
|
if (alarm_idx == ANDROID_ALARM_TYPE_COUNT && errno == ECANCELED) {
|
|
result |= ANDROID_ALARM_TIME_CHANGE_MASK;
|
|
} else {
|
|
return err;
|
|
}
|
|
} else {
|
|
result |= (1 << alarm_idx);
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
static jint android_server_AlarmManagerService_setKernelTimezone(JNIEnv*, jobject, jlong, jint minswest)
|
|
{
|
|
struct timezone tz;
|
|
|
|
tz.tz_minuteswest = minswest;
|
|
tz.tz_dsttime = 0;
|
|
|
|
int result = settimeofday(NULL, &tz);
|
|
if (result < 0) {
|
|
ALOGE("Unable to set kernel timezone to %d: %s\n", minswest, strerror(errno));
|
|
return -1;
|
|
} else {
|
|
ALOGD("Kernel timezone updated to %d minutes west of GMT\n", minswest);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static jlong init_alarm_driver()
|
|
{
|
|
int fd = open("/dev/alarm", O_RDWR);
|
|
if (fd < 0) {
|
|
ALOGV("opening alarm driver failed: %s", strerror(errno));
|
|
return 0;
|
|
}
|
|
|
|
AlarmImpl *ret = new AlarmImplAlarmDriver(fd);
|
|
return reinterpret_cast<jlong>(ret);
|
|
}
|
|
|
|
static jlong init_timerfd()
|
|
{
|
|
int epollfd;
|
|
int fds[N_ANDROID_TIMERFDS];
|
|
|
|
epollfd = epoll_create(N_ANDROID_TIMERFDS);
|
|
if (epollfd < 0) {
|
|
ALOGV("epoll_create(%u) failed: %s", N_ANDROID_TIMERFDS,
|
|
strerror(errno));
|
|
return 0;
|
|
}
|
|
|
|
for (size_t i = 0; i < N_ANDROID_TIMERFDS; i++) {
|
|
fds[i] = timerfd_create(android_alarm_to_clockid[i], 0);
|
|
if (fds[i] < 0) {
|
|
ALOGV("timerfd_create(%u) failed: %s", android_alarm_to_clockid[i],
|
|
strerror(errno));
|
|
close(epollfd);
|
|
for (size_t j = 0; j < i; j++) {
|
|
close(fds[j]);
|
|
}
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
AlarmImpl *ret = new AlarmImplTimerFd(fds, epollfd);
|
|
|
|
for (size_t i = 0; i < N_ANDROID_TIMERFDS; i++) {
|
|
epoll_event event;
|
|
event.events = EPOLLIN | EPOLLWAKEUP;
|
|
event.data.u32 = i;
|
|
|
|
int err = epoll_ctl(epollfd, EPOLL_CTL_ADD, fds[i], &event);
|
|
if (err < 0) {
|
|
ALOGV("epoll_ctl(EPOLL_CTL_ADD) failed: %s", strerror(errno));
|
|
delete ret;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
struct itimerspec spec;
|
|
memset(&spec, 0, sizeof(spec));
|
|
/* 0 = disarmed; the timerfd doesn't need to be armed to get
|
|
RTC change notifications, just set up as cancelable */
|
|
|
|
int err = timerfd_settime(fds[ANDROID_ALARM_TYPE_COUNT],
|
|
TFD_TIMER_ABSTIME | TFD_TIMER_CANCEL_ON_SET, &spec, NULL);
|
|
if (err < 0) {
|
|
ALOGV("timerfd_settime() failed: %s", strerror(errno));
|
|
delete ret;
|
|
return 0;
|
|
}
|
|
|
|
return reinterpret_cast<jlong>(ret);
|
|
}
|
|
|
|
static jlong android_server_AlarmManagerService_init(JNIEnv*, jobject)
|
|
{
|
|
jlong ret = init_alarm_driver();
|
|
if (ret) {
|
|
return ret;
|
|
}
|
|
|
|
return init_timerfd();
|
|
}
|
|
|
|
static void android_server_AlarmManagerService_close(JNIEnv*, jobject, jlong nativeData)
|
|
{
|
|
AlarmImpl *impl = reinterpret_cast<AlarmImpl *>(nativeData);
|
|
delete impl;
|
|
}
|
|
|
|
static void android_server_AlarmManagerService_set(JNIEnv*, jobject, jlong nativeData, jint type, jlong seconds, jlong nanoseconds)
|
|
{
|
|
AlarmImpl *impl = reinterpret_cast<AlarmImpl *>(nativeData);
|
|
struct timespec ts;
|
|
ts.tv_sec = seconds;
|
|
ts.tv_nsec = nanoseconds;
|
|
|
|
int result = impl->set(type, &ts);
|
|
if (result < 0)
|
|
{
|
|
ALOGE("Unable to set alarm to %lld.%09lld: %s\n", seconds, nanoseconds, strerror(errno));
|
|
}
|
|
}
|
|
|
|
static jint android_server_AlarmManagerService_waitForAlarm(JNIEnv*, jobject, jlong nativeData)
|
|
{
|
|
AlarmImpl *impl = reinterpret_cast<AlarmImpl *>(nativeData);
|
|
int result = 0;
|
|
|
|
do
|
|
{
|
|
result = impl->waitForAlarm();
|
|
} while (result < 0 && errno == EINTR);
|
|
|
|
if (result < 0)
|
|
{
|
|
ALOGE("Unable to wait on alarm: %s\n", strerror(errno));
|
|
return 0;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
static JNINativeMethod sMethods[] = {
|
|
/* name, signature, funcPtr */
|
|
{"init", "()J", (void*)android_server_AlarmManagerService_init},
|
|
{"close", "(J)V", (void*)android_server_AlarmManagerService_close},
|
|
{"set", "(JIJJ)V", (void*)android_server_AlarmManagerService_set},
|
|
{"waitForAlarm", "(J)I", (void*)android_server_AlarmManagerService_waitForAlarm},
|
|
{"setKernelTimezone", "(JI)I", (void*)android_server_AlarmManagerService_setKernelTimezone},
|
|
};
|
|
|
|
int register_android_server_AlarmManagerService(JNIEnv* env)
|
|
{
|
|
return jniRegisterNativeMethods(env, "com/android/server/AlarmManagerService",
|
|
sMethods, NELEM(sMethods));
|
|
}
|
|
|
|
} /* namespace android */
|