Android12 AlarmManagerService底层解析及开机闹钟

最近项目中需要为一个硬件实现定时开机的功能,借此机会把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,
};

图示可以如此理解:

alarmservice.cpp

dev.c

interface.c

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时间的读写分析

© 版权声明
THE END
喜欢就支持一下吧
点赞0

Warning: mysqli_query(): (HY000/3): Error writing file '/tmp/MYNZcNWb' (Errcode: 28 - No space left on device) in /www/wwwroot/583.cn/wp-includes/class-wpdb.php on line 2345
admin的头像-五八三
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

图形验证码
取消
昵称代码图片