最近项目中需求为一个硬件完成定时开机的功能,借此机会把AlarmManagerService从上层到底层的流程给梳理了一遍。
首先在应用层只能运用AlarmManager与AlarmManagerService进行通讯,就不在此赘述了。
AlarmManagerService
现在来看看AlarmManagerService的源码,在Android 12中其方位为frameworks\base\apex\jobscheduler\service\java\com\android\server\alarm
。
AlarmManager
通过aidl与AlarmManagerService
通讯。因而AlarmManagerService
会回来IAlarmManager
的IBinder
。
一般触摸得最多的是set
、setTime
和setTimeZone
,其作用分别是设置闹钟,设置时刻,设置时区。
setTime
和setTimeZone
比较简单,通过层层办法跳转,终究调用的是jni办法的setKernelTime
和setKernelTimezone
。
而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\rtc
下dev.c
的rtc_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.set
,AlarmImpl.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.c
、interface.c
和class.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.c
的rtc_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->ops
在class.c
的devm_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.c
的rtc_dev_ioctl
中能够看到,其完成已提供了设置rtc闹钟的上层完成RTC_WKALM_SET
和RTC_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.aidl
、AlarmManager.java
和AlarmManagerService.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时刻的读写分析