最近项目中需求为一个硬件完成定时开机的功能,借此机会把AlarmManagerService从上层到底层的流程给梳理了一遍。

首先在应用层只能运用AlarmManager与AlarmManagerService进行通讯,就不在此赘述了。

AlarmManagerService

现在来看看AlarmManagerService的源码,在Android 12中其方位为frameworks\base\apex\jobscheduler\service\java\com\android\server\alarm
AlarmManager 通过aidl与AlarmManagerService通讯。因而AlarmManagerService会回来IAlarmManagerIBinder

一般触摸得最多的是setsetTimesetTimeZone,其作用分别是设置闹钟,设置时刻,设置时区。

setTimesetTimeZone比较简单,通过层层办法跳转,终究调用的是jni办法的setKernelTimesetKernelTimezone

set比较复杂,通过set -> setImpl -> setImplLocked -> setImplLocked -> rescheduleKernelAlarmsLocked -> setLocked层层办法的权限、条件检查,终究走到:

private void setLocked(int type, long when) {
    // AlarmManagerService是否现已初始化
    if (mInjector.isAlarmDriverPresent()) {
        // 初始化则调用AlarmManagerService.setAlarm
        mInjector.setAlarm(type, when);
    } else {
        // 未初始化则放到handler里延迟一段时刻继续处理
        Message msg = Message.obtain();
        msg.what = AlarmHandler.ALARM_EVEN
        mHandler.removeMessages(msg.what);
        mHandler.sendMessageAtTime(msg, when);
    }
}

AlarmManagerService.setAlarm办法终究调用的是jni办法的set。至此所涉及的办法都进入jni。

AlarmManagerService的jni完成在frameworks\base\apex\jobscheduler\service\jni\com_android_server_alarm_AlarmManagerService.cpp

AlarmManagerService.cpp

jni 的办法界说如下

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},
};

jni的办法本身都只是做了一层入参的检查过滤,终究调用AlarmImpl的对应办法。

其间setKernelTime对应的是AlarmImpl.setTime,终究调用的是ioctl(fd, RTC_SET_TIME, &rtc)

int AlarmImpl::setTime(struct timeval *tv)
{
    // 调用settimeofday将时刻写入到体系
    if (settimeofday(tv, NULL) == -1) {
        ALOGV("settimeofday() failed: %s", strerror(errno));
        return -1;
    }
    // 连接rtc设备
    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;
    // 转换为utc时刻
    if (!gmtime_r(&tv->tv_sec, &tm)) {
        ALOGV("gmtime_r() failed: %s", strerror(errno));
        return -1;
    }
    // 结构为rtc标准的时刻结构
    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;
    // 通过ioctl办法将时刻设置到硬件
    if (ioctl(fd, RTC_SET_TIME, &rtc) == -1) {
        ALOGV("RTC_SET_TIME ioctl failed: %s", strerror(errno));
        return -1;
    }
    return 0;
}

ioctl会履行到对应驱动目录drivers\rtcdev.crtc_dev_ioctl办法,依据第二个入参(此处为RTC_SET_TIME)来决定要履行的办法。

setKernelTimezone只是对当前体系时刻依据传入的时区进行简单的加减,所以不会涉及到变更硬件时刻的操作:

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;
}

set办法会调用AlarmImpl.setAlarmImpl.set直接调用timerfd_settime(fds[type], TFD_TIMER_ABSTIME, &spec, NULL),创立一个体系层级的定时器,因而关机之后就失去作用了:

int AlarmImpl::set(int type, struct timespec *ts)
{
    if (static_cast<size_t>(type) > ANDROID_ALARM_TYPE_COUNT) {
        errno = EINVAL;
        return -1;
    }
    // tv_nsec和tv_sec都为 0 就会关闭闹钟,所以tv_nsec设置为 1 使闹钟仍能履行
    if (!ts->tv_nsec && !ts->tv_sec) {
        ts->tv_nsec = 1;
    }
    // 创立一个spec结构,并将ts的值复制到结构spec的it_value特点里
    struct itimerspec spec;
    memset(&spec, 0, sizeof(spec));
    memcpy(&spec.it_value, ts, sizeof(spec.it_value));
    // TFD_TIMER_ABSTIME意味着肯定时刻,依据传入时刻的年月日时分秒来设置闹钟,超越该时刻则闹钟过期
    // 可选参数有:
    // 0 相对时刻,则当前时刻 + 传入时刻
    // TFD_TIMER_CANCEL_ON_SET 同样是肯定时刻,但当时刻被从头设置时会撤销闹钟
    return timerfd_settime(fds[type], TFD_TIMER_ABSTIME, &spec, NULL);
}

驱动

其间setKernelTime对应的是AlarmImpl.setTime除了设置一个体系时刻外(settimeofday),还会调用对应的驱动将体系时刻设置到硬件中,以此确保关机再敞开时刻仍是正常活动。

在驱动中需求重视的是文件是dev.cinterface.cclass.c

class.c提供了诸如硬件分配、注册、反注册等等一些最基础的操作。interface.c主要界说了在class.c上的一些基本操作接口。dev.c则提供了更贴近应用层的一些操作函数,如ioctl办法终究的指向则界说在该文件下的rtc_dev_fops处:

static const struct file_operations rtc_dev_fops = {
    .owner        = THIS_MODULE,
    .llseek        = no_llseek,
    .read        = rtc_dev_read,
    .poll        = rtc_dev_poll,
    .unlocked_ioctl    = rtc_dev_ioctl,
#ifdef CONFIG_COMPAT
    .compat_ioctl    = rtc_dev_compat_ioctl,
#endif
    .open        = rtc_dev_open,
    .release    = rtc_dev_release,
    .fasync        = rtc_dev_fasync,
};

图示能够如此了解:

graph LR
node(alarmservice.cpp)-->node1(dev.c)-->node2(interface.c)-->node3(class.c)

因而AlarmImpl.setTime最后调用了ioctl(fd, RTC_SET_TIME, &rtc)。会履行dev.crtc_dev_ioctl,并依据RTC_SET_TIME履行其case分支的:

case RTC_SET_TIME:
    mutex_unlock(&rtc->ops_lock);
    if (copy_from_user(&tm, uarg, sizeof(tm)))
        return -EFAULT;
    return rtc_set_time(rtc, &tm);

rtc_set_time的办法界说在interface.c处,需求重视的方位是:

...
if (!rtc->ops)
    err = -ENODEV;
    // ops是否有set_time办法
else if (rtc->ops->set_time)
    // 履行ops的set_time办法
    err = rtc->ops->set_time(rtc->dev.parent, tm);
else
    err = -EINVAL;
...

rtc->opsclass.cdevm_rtc_device_register中进行初始化,则在rtc设备注册的时分会创立一个rtc_device结构并将实际的rtc设备赋值到特点ops下,因而这里的ops能够了解为操作对应的驱动文件:

struct rtc_device *devm_rtc_device_register(struct device *dev,
                        const char *name,
                        const struct rtc_class_ops *ops,
                        struct module *owner)
{
    struct rtc_device *rtc;
    int err;
    rtc = devm_rtc_allocate_device(dev);
    if (IS_ERR(rtc))
        return rtc;
    rtc->ops = ops;
    err = __rtc_register_device(owner, rtc);
    if (err)
        return ERR_PTR(err);
    return rtc;
}

开机闹钟

dev.crtc_dev_ioctl中能够看到,其完成已提供了设置rtc闹钟的上层完成RTC_WKALM_SETRTC_ALM_SET,这里咱们运用RTC_WKALM_SET,至于为什么不用RTC_ALM_SET,则能够参阅其case下的注释:

/* RTC_ALM_SET alarms may be up to 24 hours in the future.
 * Rather than expecting every RTC to implement "don't care"
 * for day/month/year fields, just force the alarm to have
 * the right values for those fields.
 *
 * 主要是RTC_WKALM_SET会主动使能闹钟中断
 * RTC_WKALM_SET should be used instead.  Not only does it
 * eliminate the need for a separate RTC_AIE_ON call, it
 * doesn't have the "alarm 23:59:59 in the future" race.
 *
 * NOTE:  some legacy code may have used invalid fields as
 * wildcards, exposing hardware "periodic alarm" capabilities.
 * Not supported here.
 */

先在IAlarmManager.aidlAlarmManager.javaAlarmManagerService.java中增加对应的接口供应用层调用:

// IAlarmManager.aidl
boolean setPowerOnAlarm(long millis);
long getPowerOnAlarm();
// AlarmManager.java
public void setPowerOnAlarm(long millis) {
    try {
        mService.setPowerOnAlarm(millis);
    } catch (RemoteException ex) {
        throw ex.rethrowFromSystemServer();
    }
}
public long getPowerOnAlarm() {
    try {
        mService.getPowerOnAlarm();
    } catch (RemoteException ex) {
        throw ex.rethrowFromSystemServer();
    }
    return -1;
}

AlarmManagerService.java平分多个部分增加

// IBinder mService
@Override
public boolean setPowerOnAlarm(long millis) {
    getContext().enforceCallingOrSelfPermission(
            "android.permission.SET_TIME",
            "setPowerOnA
    return setPowerOnAlarmImpl(millis);
@Override
public long getPowerOnAlarm() {
    getContext().enforceCallingOrSelfPermission(
            "android.permission.SET_TIME",
            "getPowerOnAlarm");
    long millis = -1;
    synchronized (mLock) {
        millis = mInjector.getPowerOnAlarm();
        Slog.i(TAG, "Reading power on alarm of rtc: " + millis);
    }
    return millis;
}
// AlarmManagerService.java
private static native int setPowerOnAlarm(long nativeData, long millis);
private static native long getPowerOnAlarm(long nativeData);
boolean setPowerOnAlarmImpl(long millis) {
    synchronized (mLock) {
    	mInjector.setPowerOnAlarm(millis);
        return true;
    }
}
// class Injector
void setPowerOnAlarm(long millis) {
    if (mNativeData != 0) {
        AlarmManagerService.setPowerOnAlarm(mNativeData, millis);
    }
}
long getPowerOnAlarm() {
    if (mNativeData != 0) {
        return AlarmManagerService.getPowerOnAlarm(mNativeData);
    }
    return -1;
}

终究调用次序为

mService.setPowerOnAlarm->setPowerOnAlarmImpl->Injector.setPowerOnAlarm

->native setPowerOnAlarm

jni中声明两个办法:

// 设置开机闹钟
{"setPowerOnAlarm", "(JJ)I", (void*)android_server_alarm_AlarmManagerService_setPowerOnAlarm},
// 获取已设置的开机闹钟
{"getPowerOnAlarm", "(J)J", (void*)android_server_alarm_AlarmManagerService_getPowerOnAlarm},

其对应的完成,便是直接调用AlarmImpl的相关办法:

static jint android_server_alarm_AlarmManagerService_setPowerOnAlarm(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);
    int ret = impl->setPowerOnAlarm(&tv);
    if (ret < 0) {
        ALOGW("Unable to set power on alarm rtc to %ld: %s", tv.tv_sec, strerror(errno));
        ret = -1;
    }
    return ret;
}
static jlong android_server_alarm_AlarmManagerService_getPowerOnAlarm(JNIEnv*, jobject, jlong nativeData)
{
    AlarmImpl *impl = reinterpret_cast<AlarmImpl *>(nativeData);
    return impl->getPowerOnAlarm();
}

AlarmImpl中增加完成,这里需求重视setPowerOnAlarm办法中将rtc用rtc_wkalrm包裹一次,并设置其enabled为 1:

int AlarmImpl::setPowerOnAlarm(struct timeval *tv)
{
    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;
    }
    char timeStr[80];
    strftime(timeStr, sizeof(timeStr), "%Y-%m-%d %H:%M:%S", &tm);
    // 创立rtc_time结构来设置闹钟的时刻
    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;
    struct rtc_wkalrm alarm = {};
    // 设置闹钟的enabled为 1
    alarm.enabled = 1;
    alarm.time = rtc;
    if (ioctl(fd, RTC_WKALM_SET, &alarm) == -1) {
        ALOGV("RTC_SET_TIME ioctl failed: %s", strerror(errno));
        return -1;
    }
    return 0;
}
long AlarmImpl::getPowerOnAlarm() {
    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;
    }
    return ioctl(fd, RTC_WKALM_RD, NULL);
}

alarm.enabled = 1对应的是interface.c中的

int rtc_set_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alarm)
{
    int err;
    if (!rtc->ops)
        return -ENODEV;
    else if (!rtc->ops->set_alarm)
        return -EINVAL;
    err = rtc_valid_tm(&alarm->time);
    if (err != 0)
        return err;
    err = rtc_valid_range(rtc, &alarm->time);
    if (err)
        return err;
    err = mutex_lock_interruptible(&rtc->ops_lock);
    if (err)
        return err;
    // aie_timer是rtc中的一个定时器
    // 清理掉上一个设置的定时器
    if (rtc->aie_timer.enabled)
        rtc_timer_remove(rtc, &rtc->aie_timer);
    // 将闹钟时刻转换为内核时刻,设置到定时器中
    rtc->aie_timer.node.expires = rtc_tm_to_ktime(alarm->time);
    // 设置为到期只履行一次,周期履行规则留给应用层处理
    rtc->aie_timer.period = 0;
    // 假如没有设置enabled特点为 1,则闹钟不会被增加到rtc的定时器队列中
    // 则闹钟到期后也不会履行对应的办法
    if (alarm->enabled)
        err = rtc_timer_enqueue(rtc, &rtc->aie_timer);
    mutex_unlock(&rtc->ops_lock);
    return err;
}

在驱动的probe初始化中增加以下复位逻辑

err = pcf8563_get_alarm_mode(client, NULL, &alm_pending);
if (err) {
    dev_err(&client->dev, "%s: read error\n", __func__);
    return err;
}
// 开机后清空闹钟
if (alm_pending)
    pcf8563_set_alarm_mode(client, 0);

参阅内容

linux timerfd系列函数总结

Linux下RTC时刻的读写分析