From a1d6f92f34adb499565c9513235227a308e90221 Mon Sep 17 00:00:00 2001 From: Greg Hackmann Date: Mon, 9 Dec 2013 16:56:53 -0800 Subject: [PATCH] Add timerfd backend to AlarmManagerService 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 --- .../com/android/server/AlarmManagerService.java | 28 +-- .../jni/com_android_server_AlarmManagerService.cpp | 235 +++++++++++++++++++-- 2 files changed, 235 insertions(+), 28 deletions(-) diff --git a/services/java/com/android/server/AlarmManagerService.java b/services/java/com/android/server/AlarmManagerService.java index 5ae9a6df01ad..2e1b0af7b099 100644 --- a/services/java/com/android/server/AlarmManagerService.java +++ b/services/java/com/android/server/AlarmManagerService.java @@ -99,7 +99,7 @@ class AlarmManagerService extends IAlarmManager.Stub { private Object mLock = new Object(); - private int mDescriptor; + private long mNativeData; private long mNextWakeup; private long mNextNonWakeup; private int mBroadcastRefCount = 0; @@ -464,7 +464,7 @@ class AlarmManagerService extends IAlarmManager.Stub { public AlarmManagerService(Context context) { mContext = context; - mDescriptor = init(); + mNativeData = init(); mNextWakeup = mNextNonWakeup = 0; // We have to set current TimeZone info to kernel @@ -493,7 +493,7 @@ class AlarmManagerService extends IAlarmManager.Stub { mClockReceiver.scheduleDateChangedEvent(); mUninstallReceiver = new UninstallReceiver(); - if (mDescriptor != -1) { + if (mNativeData != 0) { mWaitThread.start(); } else { Slog.w(TAG, "Failed to open alarm driver. Falling back to a handler."); @@ -502,7 +502,7 @@ class AlarmManagerService extends IAlarmManager.Stub { protected void finalize() throws Throwable { try { - close(mDescriptor); + close(mNativeData); } finally { super.finalize(); } @@ -702,7 +702,7 @@ class AlarmManagerService extends IAlarmManager.Stub { // Update the kernel timezone information // Kernel tracks time offsets as 'minutes west of GMT' int gmtOffset = zone.getOffset(System.currentTimeMillis()); - setKernelTimezone(mDescriptor, -(gmtOffset / 60000)); + setKernelTimezone(mNativeData, -(gmtOffset / 60000)); } TimeZone.setDefault(null); @@ -796,7 +796,7 @@ class AlarmManagerService extends IAlarmManager.Stub { private void setLocked(int type, long when) { - if (mDescriptor != -1) + if (mNativeData != 0) { // The kernel never triggers alarms with negative wakeup times // so we ensure they are positive. @@ -809,7 +809,7 @@ class AlarmManagerService extends IAlarmManager.Stub { alarmNanoseconds = (when % 1000) * 1000 * 1000; } - set(mDescriptor, type, alarmSeconds, alarmNanoseconds); + set(mNativeData, type, alarmSeconds, alarmNanoseconds); } else { @@ -1014,11 +1014,11 @@ class AlarmManagerService extends IAlarmManager.Stub { } } - private native int init(); - private native void close(int fd); - private native void set(int fd, int type, long seconds, long nanoseconds); - private native int waitForAlarm(int fd); - private native int setKernelTimezone(int fd, int minuteswest); + private native long init(); + private native void close(long nativeData); + private native void set(long nativeData, int type, long seconds, long nanoseconds); + private native int waitForAlarm(long nativeData); + private native int setKernelTimezone(long nativeData, int minuteswest); private void triggerAlarmsLocked(ArrayList triggerList, long nowELAPSED, long nowRTC) { // batches are temporally sorted, so we need only pull from the @@ -1158,7 +1158,7 @@ class AlarmManagerService extends IAlarmManager.Stub { while (true) { - int result = waitForAlarm(mDescriptor); + int result = waitForAlarm(mNativeData); triggerList.clear(); @@ -1340,7 +1340,7 @@ class AlarmManagerService extends IAlarmManager.Stub { // daylight savings information. TimeZone zone = TimeZone.getTimeZone(SystemProperties.get(TIMEZONE_PROPERTY)); int gmtOffset = zone.getOffset(System.currentTimeMillis()); - setKernelTimezone(mDescriptor, -(gmtOffset / 60000)); + setKernelTimezone(mNativeData, -(gmtOffset / 60000)); scheduleDateChangedEvent(); } } diff --git a/services/jni/com_android_server_AlarmManagerService.cpp b/services/jni/com_android_server_AlarmManagerService.cpp index 8da143d8f1c7..342515bb3bb0 100644 --- a/services/jni/com_android_server_AlarmManagerService.cpp +++ b/services/jni/com_android_server_AlarmManagerService.cpp @@ -25,6 +25,8 @@ #include #include #include +#include +#include #include #include #include @@ -37,7 +39,136 @@ namespace android { -static jint android_server_AlarmManagerService_setKernelTimezone(JNIEnv*, jobject, jint, jint minswest) +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; @@ -55,36 +186,112 @@ static jint android_server_AlarmManagerService_setKernelTimezone(JNIEnv*, jobjec return 0; } -static jint android_server_AlarmManagerService_init(JNIEnv*, jobject) +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(ret); +} + +static jlong init_timerfd() { - return open("/dev/alarm", O_RDWR); + 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(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, jint fd) +static void android_server_AlarmManagerService_close(JNIEnv*, jobject, jlong nativeData) { - close(fd); + AlarmImpl *impl = reinterpret_cast(nativeData); + delete impl; } -static void android_server_AlarmManagerService_set(JNIEnv*, jobject, jint fd, jint type, jlong seconds, jlong nanoseconds) +static void android_server_AlarmManagerService_set(JNIEnv*, jobject, jlong nativeData, jint type, jlong seconds, jlong nanoseconds) { + AlarmImpl *impl = reinterpret_cast(nativeData); struct timespec ts; ts.tv_sec = seconds; ts.tv_nsec = nanoseconds; - int result = ioctl(fd, ANDROID_ALARM_SET(type), &ts); + 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, jint fd) +static jint android_server_AlarmManagerService_waitForAlarm(JNIEnv*, jobject, jlong nativeData) { + AlarmImpl *impl = reinterpret_cast(nativeData); int result = 0; do { - result = ioctl(fd, ANDROID_ALARM_WAIT); + result = impl->waitForAlarm(); } while (result < 0 && errno == EINTR); if (result < 0) @@ -98,11 +305,11 @@ static jint android_server_AlarmManagerService_waitForAlarm(JNIEnv*, jobject, ji static JNINativeMethod sMethods[] = { /* name, signature, funcPtr */ - {"init", "()I", (void*)android_server_AlarmManagerService_init}, - {"close", "(I)V", (void*)android_server_AlarmManagerService_close}, - {"set", "(IIJJ)V", (void*)android_server_AlarmManagerService_set}, - {"waitForAlarm", "(I)I", (void*)android_server_AlarmManagerService_waitForAlarm}, - {"setKernelTimezone", "(II)I", (void*)android_server_AlarmManagerService_setKernelTimezone}, + {"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) -- 2.11.0