Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit eb0ca2b7 authored by Greg Hackmann's avatar Greg Hackmann Committed by Gerrit Code Review
Browse files

Merge "Add timerfd backend to AlarmManagerService"

parents 9f4e67c5 a1d6f92f
Loading
Loading
Loading
Loading
+14 −14
Original line number Diff line number Diff line
@@ -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();
            }
        }
+221 −14
Original line number Diff line number Diff line
@@ -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()
{
    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 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, 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)