diff options
author | Greg Hackmann <ghackmann@google.com> | 2013-12-17 10:15:33 -0800 |
---|---|---|
committer | Android Git Automerger <android-git-automerger@android.com> | 2013-12-17 10:15:33 -0800 |
commit | 060ae1db7bba39cb8171f4267b5de9538f1ab440 (patch) | |
tree | 209b2156ca7eee0ca134cbc57bd69c85d54d94ef /services | |
parent | 3545433d052769417dcdd70bc59fbf31f9925ba4 (diff) | |
parent | eb0ca2b7bfd70fbbf2bd730b1c431faac21d0416 (diff) | |
download | frameworks_base-060ae1db7bba39cb8171f4267b5de9538f1ab440.zip frameworks_base-060ae1db7bba39cb8171f4267b5de9538f1ab440.tar.gz frameworks_base-060ae1db7bba39cb8171f4267b5de9538f1ab440.tar.bz2 |
am eb0ca2b7: Merge "Add timerfd backend to AlarmManagerService"
* commit 'eb0ca2b7bfd70fbbf2bd730b1c431faac21d0416':
Add timerfd backend to AlarmManagerService
Diffstat (limited to 'services')
-rw-r--r-- | services/java/com/android/server/AlarmManagerService.java | 28 | ||||
-rw-r--r-- | services/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 5ae9a6d..2e1b0af 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<Alarm> 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 8da143d..342515b 100644 --- a/services/jni/com_android_server_AlarmManagerService.cpp +++ b/services/jni/com_android_server_AlarmManagerService.cpp @@ -25,6 +25,8 @@ #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> @@ -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<jlong>(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<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, jint fd) +static void android_server_AlarmManagerService_close(JNIEnv*, jobject, jlong nativeData) { - close(fd); + AlarmImpl *impl = reinterpret_cast<AlarmImpl *>(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<AlarmImpl *>(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<AlarmImpl *>(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) |