最近项目中需要为一个硬件实现定时开机的功能,借此机会把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,
};
图示可以如此理解:
因此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);