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 <ghackmann@google.com>
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 @@
 
     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 @@
     
     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 @@
         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 @@
     
     protected void finalize() throws Throwable {
         try {
-            close(mDescriptor);
+            close(mNativeData);
         } finally {
             super.finalize();
         }
@@ -702,7 +702,7 @@
                 // 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 @@
 
     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 @@
                 alarmNanoseconds = (when % 1000) * 1000 * 1000;
             }
             
-            set(mDescriptor, type, alarmSeconds, alarmNanoseconds);
+            set(mNativeData, type, alarmSeconds, alarmNanoseconds);
         }
         else
         {
@@ -1014,11 +1014,11 @@
         }
     }
 
-    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 @@
 
             while (true)
             {
-                int result = waitForAlarm(mDescriptor);
+                int result = waitForAlarm(mNativeData);
 
                 triggerList.clear();
 
@@ -1340,7 +1340,7 @@
                 // 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 @@
     return 0;
 }
 
-static jint android_server_AlarmManagerService_init(JNIEnv*, jobject)
+static jlong init_alarm_driver()
 {
-    return open("/dev/alarm", O_RDWR);
+    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 void android_server_AlarmManagerService_close(JNIEnv*, jobject, jint fd)
+static jlong init_timerfd()
 {
-    close(fd);
+    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 void android_server_AlarmManagerService_set(JNIEnv*, jobject, jint fd, jint type, jlong seconds, jlong nanoseconds)
+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 = 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 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)