Moving alarm manager native code to apex

Extracting out alarm manager jni from libandroid_servers to its own
library.

Test: lunch coral-userdebug && make -j
lunch bertha_arm-userdebug && make -j
atest CtsAlarmManagerTestCases

Bug: 151976605
Change-Id: Ie9e15b2665a2c51c9b2413af304e228f8a3ae2fc
diff --git a/apex/jobscheduler/service/Android.bp b/apex/jobscheduler/service/Android.bp
index 47267df..8aa88c2 100644
--- a/apex/jobscheduler/service/Android.bp
+++ b/apex/jobscheduler/service/Android.bp
@@ -19,4 +19,8 @@
 
     // Rename classes shared with the framework
     jarjar_rules: "jarjar-rules.txt",
+
+    required: [
+        "libalarm_jni",
+    ],
 }
diff --git a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
index 3e43dcb..5ca3b33 100644
--- a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
@@ -3831,6 +3831,7 @@
         }
 
         void init() {
+            System.loadLibrary("alarm_jni");
             mNativeData = AlarmManagerService.init();
         }
 
diff --git a/apex/jobscheduler/service/jni/Android.bp b/apex/jobscheduler/service/jni/Android.bp
new file mode 100644
index 0000000..c502867
--- /dev/null
+++ b/apex/jobscheduler/service/jni/Android.bp
@@ -0,0 +1,41 @@
+cc_library_shared {
+    name: "libalarm_jni",
+
+    cpp_std: "c++2a",
+    cflags: [
+        "-Wall",
+        "-Werror",
+        "-Wno-unused-parameter",
+        "-Wthread-safety",
+    ],
+
+    srcs: [
+        "com_android_server_alarm_AlarmManagerService.cpp",
+        "onload.cpp",
+    ],
+
+    shared_libs: [
+         "libnativehelper",
+         "liblog",
+         "libbase",
+    ],
+
+    product_variables: {
+        arc: {
+            exclude_srcs: [
+                "com_android_server_alarm_AlarmManagerService.cpp",
+            ],
+            srcs: [
+                ":arctimersrcs",
+            ],
+        }
+    }
+
+}
+
+filegroup {
+    name: "lib_alarmManagerService_native",
+    srcs: [
+        "com_android_server_alarm_AlarmManagerService.cpp",
+    ],
+}
diff --git a/apex/jobscheduler/service/jni/com_android_server_alarm_AlarmManagerService.cpp b/apex/jobscheduler/service/jni/com_android_server_alarm_AlarmManagerService.cpp
new file mode 100644
index 0000000..8f9e187
--- /dev/null
+++ b/apex/jobscheduler/service/jni/com_android_server_alarm_AlarmManagerService.cpp
@@ -0,0 +1,406 @@
+/*
+** 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 <android-base/file.h>
+#include <android-base/unique_fd.h>
+#include <nativehelper/JNIHelp.h>
+#include <utils/Log.h>
+#include <utils/String8.h>
+#include <utils/misc.h>
+#include "jni.h"
+
+#include <dirent.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/rtc.h>
+
+#include <array>
+#include <limits>
+#include <memory>
+
+namespace android {
+
+static constexpr int ANDROID_ALARM_TIME_CHANGE_MASK = 1 << 16;
+
+/**
+ * The AlarmManager alarm constants:
+ *
+ *   RTC_WAKEUP
+ *   RTC
+ *   REALTIME_WAKEUP
+ *   REALTIME
+ *   SYSTEMTIME (only defined in old alarm driver header, possibly unused?)
+ *
+ * We also need an extra CLOCK_REALTIME fd which exists specifically to be
+ * canceled on RTC changes.
+ */
+static const size_t ANDROID_ALARM_TYPE_COUNT = 5;
+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,
+};
+
+typedef std::array<int, N_ANDROID_TIMERFDS> TimerFds;
+
+class AlarmImpl
+{
+public:
+    AlarmImpl(const TimerFds &fds, int epollfd, const std::string &rtc_dev)
+          : fds{fds}, epollfd{epollfd}, rtc_dev{rtc_dev} {}
+    ~AlarmImpl();
+
+    int set(int type, struct timespec *ts);
+    int setTime(struct timeval *tv);
+    int waitForAlarm();
+    int getTime(int type, struct itimerspec *spec);
+
+private:
+    const TimerFds fds;
+    const int epollfd;
+    std::string rtc_dev;
+};
+
+AlarmImpl::~AlarmImpl()
+{
+    for (auto fd : fds) {
+        epoll_ctl(epollfd, EPOLL_CTL_DEL, fd, nullptr);
+        close(fd);
+    }
+
+    close(epollfd);
+}
+
+int AlarmImpl::set(int type, struct timespec *ts)
+{
+    if (static_cast<size_t>(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 AlarmImpl::getTime(int type, struct itimerspec *spec)
+{
+    if (static_cast<size_t>(type) > ANDROID_ALARM_TYPE_COUNT) {
+        errno = EINVAL;
+        return -1;
+    }
+
+    return timerfd_gettime(fds[type], spec);
+}
+
+int AlarmImpl::setTime(struct timeval *tv)
+{
+    if (settimeofday(tv, NULL) == -1) {
+        ALOGV("settimeofday() failed: %s", strerror(errno));
+        return -1;
+    }
+
+    android::base::unique_fd fd{open(rtc_dev.c_str(), O_RDWR)};
+    if (!fd.ok()) {
+        ALOGE("Unable to open %s: %s", rtc_dev.c_str(), strerror(errno));
+        return -1;
+    }
+
+    struct tm tm;
+    if (!gmtime_r(&tv->tv_sec, &tm)) {
+        ALOGV("gmtime_r() failed: %s", strerror(errno));
+        return -1;
+    }
+
+    struct rtc_time rtc = {};
+    rtc.tm_sec = tm.tm_sec;
+    rtc.tm_min = tm.tm_min;
+    rtc.tm_hour = tm.tm_hour;
+    rtc.tm_mday = tm.tm_mday;
+    rtc.tm_mon = tm.tm_mon;
+    rtc.tm_year = tm.tm_year;
+    rtc.tm_wday = tm.tm_wday;
+    rtc.tm_yday = tm.tm_yday;
+    rtc.tm_isdst = tm.tm_isdst;
+    if (ioctl(fd, RTC_SET_TIME, &rtc) == -1) {
+        ALOGV("RTC_SET_TIME ioctl failed: %s", strerror(errno));
+        return -1;
+    }
+
+    return 0;
+}
+
+int AlarmImpl::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));
+        // Worth evaluating even if read fails with EAGAIN, since epoll_wait
+        // returned. (see b/78560047#comment34)
+        if (err < 0 && errno != EAGAIN) {
+            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_alarm_AlarmManagerService_setKernelTime(JNIEnv*, jobject, jlong nativeData, jlong millis)
+{
+    AlarmImpl *impl = reinterpret_cast<AlarmImpl *>(nativeData);
+
+    if (millis <= 0 || millis / 1000LL >= std::numeric_limits<time_t>::max()) {
+        return -1;
+    }
+
+    struct timeval tv;
+    tv.tv_sec = (millis / 1000LL);
+    tv.tv_usec = ((millis % 1000LL) * 1000LL);
+
+    ALOGD("Setting time of day to sec=%ld", tv.tv_sec);
+
+    int ret = impl->setTime(&tv);
+    if (ret < 0) {
+        ALOGW("Unable to set rtc to %ld: %s", tv.tv_sec, strerror(errno));
+        ret = -1;
+    }
+    return ret;
+}
+
+static jint android_server_alarm_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 void log_timerfd_create_error(clockid_t id)
+{
+    if (errno == EINVAL) {
+        switch (id) {
+        case CLOCK_REALTIME_ALARM:
+        case CLOCK_BOOTTIME_ALARM:
+            ALOGE("kernel missing required commits:");
+            ALOGE("https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=6cffe00f7d4e24679eae6b7aae4caaf915288256");
+            ALOGE("https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=11ffa9d6065f344a9bd769a2452f26f2f671e5f8");
+            LOG_ALWAYS_FATAL("kernel does not support timerfd_create() with alarm timers");
+            break;
+
+        case CLOCK_BOOTTIME:
+            ALOGE("kernel missing required commit:");
+            ALOGE("https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=4a2378a943f09907fb1ae35c15de917f60289c14");
+            LOG_ALWAYS_FATAL("kernel does not support timerfd_create(CLOCK_BOOTTIME)");
+            break;
+
+        default:
+            break;
+        }
+    }
+
+    ALOGE("timerfd_create(%u) failed: %s", id, strerror(errno));
+}
+
+static jlong android_server_alarm_AlarmManagerService_init(JNIEnv*, jobject)
+{
+    int epollfd;
+    TimerFds fds;
+
+    epollfd = epoll_create(fds.size());
+    if (epollfd < 0) {
+        ALOGE("epoll_create(%zu) failed: %s", fds.size(), strerror(errno));
+        return 0;
+    }
+
+    for (size_t i = 0; i < fds.size(); i++) {
+        fds[i] = timerfd_create(android_alarm_to_clockid[i], TFD_NONBLOCK);
+        if (fds[i] < 0) {
+            log_timerfd_create_error(android_alarm_to_clockid[i]);
+            close(epollfd);
+            for (size_t j = 0; j < i; j++) {
+                close(fds[j]);
+            }
+            return 0;
+        }
+    }
+
+    // Find the wall clock RTC. We expect this always to be /dev/rtc0, but
+    // check the /dev/rtc symlink first so that legacy devices that don't use
+    // rtc0 can add a symlink rather than need to carry a local patch to this
+    // code.
+    //
+    // TODO: if you're reading this in a world where all devices are using the
+    // GKI, you can remove the readlink and just assume /dev/rtc0.
+    std::string dev_rtc;
+    if (!android::base::Readlink("/dev/rtc", &dev_rtc)) {
+        dev_rtc = "/dev/rtc0";
+    }
+
+    std::unique_ptr<AlarmImpl> alarm{new AlarmImpl(fds, epollfd, dev_rtc)};
+
+    for (size_t i = 0; i < fds.size(); 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) {
+            ALOGE("epoll_ctl(EPOLL_CTL_ADD) failed: %s", strerror(errno));
+            return 0;
+        }
+    }
+
+    struct itimerspec 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) {
+        ALOGE("timerfd_settime() failed: %s", strerror(errno));
+        return 0;
+    }
+
+    return reinterpret_cast<jlong>(alarm.release());
+}
+
+static jlong android_server_alarm_AlarmManagerService_getNextAlarm(JNIEnv*, jobject, jlong nativeData, jint type)
+{
+    AlarmImpl *impl = reinterpret_cast<AlarmImpl *>(nativeData);
+    struct itimerspec spec;
+    memset(&spec, 0, sizeof(spec));
+    const int result = impl->getTime(type, &spec);
+    if (result < 0)
+    {
+        ALOGE("timerfd_gettime() failed for fd %d: %s\n", static_cast<int>(type), strerror(errno));
+        return result;
+    }
+    struct timespec nextTimespec = spec.it_value;
+    long long millis = nextTimespec.tv_sec * 1000LL;
+    millis += (nextTimespec.tv_nsec / 1000000LL);
+    return static_cast<jlong>(millis);
+}
+
+static void android_server_alarm_AlarmManagerService_close(JNIEnv*, jobject, jlong nativeData)
+{
+    AlarmImpl *impl = reinterpret_cast<AlarmImpl *>(nativeData);
+    delete impl;
+}
+
+static jint android_server_alarm_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;
+
+    const int result = impl->set(type, &ts);
+    if (result < 0)
+    {
+        ALOGE("Unable to set alarm to %lld.%09lld: %s\n",
+              static_cast<long long>(seconds),
+              static_cast<long long>(nanoseconds), strerror(errno));
+    }
+    return result >= 0 ? 0 : errno;
+}
+
+static jint android_server_alarm_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 const JNINativeMethod sMethods[] = {
+     /* name, signature, funcPtr */
+    {"init", "()J", (void*)android_server_alarm_AlarmManagerService_init},
+    {"close", "(J)V", (void*)android_server_alarm_AlarmManagerService_close},
+    {"set", "(JIJJ)I", (void*)android_server_alarm_AlarmManagerService_set},
+    {"waitForAlarm", "(J)I", (void*)android_server_alarm_AlarmManagerService_waitForAlarm},
+    {"setKernelTime", "(JJ)I", (void*)android_server_alarm_AlarmManagerService_setKernelTime},
+    {"setKernelTimezone", "(JI)I", (void*)android_server_alarm_AlarmManagerService_setKernelTimezone},
+    {"getNextAlarm", "(JI)J", (void*)android_server_alarm_AlarmManagerService_getNextAlarm},
+};
+
+int register_android_server_alarm_AlarmManagerService(JNIEnv* env)
+{
+    return jniRegisterNativeMethods(env, "com/android/server/alarm/AlarmManagerService",
+                                    sMethods, NELEM(sMethods));
+}
+
+} /* namespace android */
diff --git a/apex/jobscheduler/service/jni/onload.cpp b/apex/jobscheduler/service/jni/onload.cpp
new file mode 100644
index 0000000..f40413f
--- /dev/null
+++ b/apex/jobscheduler/service/jni/onload.cpp
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+#include <nativehelper/JNIHelp.h>
+#include "jni.h"
+#include "utils/Log.h"
+#include "utils/misc.h"
+
+namespace android {
+int register_android_server_alarm_AlarmManagerService(JNIEnv* env);
+};
+
+using namespace android;
+
+extern "C" jint JNI_OnLoad(JavaVM* vm, void* /* reserved */)
+{
+    JNIEnv* env = NULL;
+    jint result = -1;
+
+    if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
+        ALOGE("GetEnv failed!");
+        return result;
+    }
+    ALOG_ASSERT(env, "Could not retrieve the env!");
+
+    register_android_server_alarm_AlarmManagerService(env);
+    return JNI_VERSION_1_4;
+}