本文共 11840 字,大约阅读时间需要 39 分钟。
在android中,定时alarm功能是很常用的,现在来分析下怎么实现的,这里将采用自下而上的方式讲解。
1. driver
首先了解下android下是如何driver一个RTC的。android RTC 的driver 位于kernel\drivers\rtc 目录下。
在该目录下,有一堆以rtc-为前缀的文件,这些文件都是各种板子上用的rtc底层驱动代码,我们要看的只有3个,rtc-s3c.c ,alarm.c, alarm-dev.c 。
看下第一个,rtc-s3c.c 是三星产的arm芯片所专用的一个rtc驱动,看看怎么实现:
它用的是平台设备驱动,
static struct platform_driver s3c_rtc_driver = {
.probe = s3c_rtc_probe,
.remove = __devexit_p(s3c_rtc_remove),
.suspend = s3c_rtc_suspend,
.resume = s3c_rtc_resume,
.id_table = s3c_rtc_driver_ids,
.driver = {
.name = "s3c-rtc",
.owner = THIS_MODULE,
},
};
static const struct rtc_class_ops s3c_rtcops = {
.open = s3c_rtc_open,
.release = s3c_rtc_release,
.read_time = s3c_rtc_gettime,
.set_time = s3c_rtc_settime,
.read_alarm = s3c_rtc_getalarm,
.set_alarm = s3c_rtc_setalarm,
.proc = s3c_rtc_proc,
.alarm_irq_enable = s3c_rtc_setaie,
};
以上这两个结构体,第一个结构体是平台设备中的driver部分,第二个结构体被顺利注册进rtc子系统。Rtc的所用到的结构体被定义在kernel\include\linux\rtc.h里面。
struct rtc_device { struct device dev; struct module *owner;
int id; char name[RTC_DEVICE_NAME_SIZE];
const struct rtc_class_ops *ops; struct mutex ops_lock;
struct cdev char_dev; unsigned long flags;
unsigned long irq_data; spinlock_t irq_lock; wait_queue_head_t irq_queue; struct fasync_struct *async_queue;
struct rtc_task *irq_task; spinlock_t irq_task_lock; int irq_freq; int max_user_freq;
struct timerqueue_head timerqueue; struct rtc_timer aie_timer; struct rtc_timer uie_rtctimer; struct hrtimer pie_timer; /* sub second exp, so needs hrtimer */ int pie_enabled; struct work_struct irqwork;
#ifdef CONFIG_RTC_INTF_DEV_UIE_EMUL struct work_struct uie_task; struct timer_list uie_timer; /* Those fields are protected by rtc->irq_lock */ unsigned int oldsecs; unsigned int uie_irq_active:1; unsigned int stop_uie_polling:1; unsigned int uie_task_active:1; unsigned int uie_timer_active:1; #endif };
这个结构体是核心部分,内核中就是靠它传递信息,不管在哪使用,都要靠它间接的调用底层信息。比如在alarm.c 中。
多次使用了rtc_set_time/rtc_get_time,这些函数虽然是定义在rtc目录下的interface.c 中,但实质还是rtc-s3c.c中结构体 rtc_class_ops所指过去的函数。
那么为什么多了一个alarm.c ,因为在android中它为了使得平台无关性提高,因此大量的增加过渡代码层,HAL就是这样诞生的。
alarm.c在用户空间中会多一个/dev/alarm 节点,而rtc-s3c.c 会产生/dev/rtc这样的节点。
Android在HAL层中,是对/dev/alarm这个结点进行操作。
3、JNI 的实现
com_android_server_AlarmManagerService.cpp 位于frameworks\base\services\jni 目录下,部分代码如下:
static jint android_server_AlarmManagerService_init(JNIEnv* env, jobject obj)
{
return open("/dev/alarm", O_RDWR); }
static void android_server_AlarmManagerService_set(JNIEnv* env, jobject obj, jint fd, jint type, jlong nanoseconds)
{
struct timespec ts;
ts.tv_sec = NANOSECONDS_TO_SECONDS(nanoseconds);
ts.tv_nsec = nanoseconds - SECONDS_TO_NANOSECONDS(ts.tv_sec);
int result = ioctl(fd, ANDROID_ALARM_SET(type), &ts); }
static JNINativeMethod sMethods[] = {
/* name, signature, funcPtr */
{"init", "()I", (void*)android_server_AlarmManagerService_init}, {"close", "(I)V", (void*)android_server_AlarmManagerService_close}, {"set", "(IIJ)V", (void*)android_server_AlarmManagerService_set}, };
JNI就是按照特定写法的JAVA版的linux c应用程序。
4、 framework层
frameworks/base/services/java/com/android/server/AlarmManagerService.java
frameworks/base/core/java/android/app/AlarmManager.java
AlarmManager是直接提供给app层的API接口,它是AlarmManagerService.java的一个封装。
AlarmManagerService是把上面分析的JNI拿来在此调用。然后包装一下,将功能实现得更完美些。
下面是 AlarmManagerService这个类中摘出来的小段:
private native int init();
private native void close(int fd);
private native void set(int fd, int type, long nanoseconds);
private native int waitForAlarm(int fd);
private native int setKernelTimezone(int fd, int minuteswest);
这些就是JNI实现过来的接口。
5、APP层
\packages\apps\DeskClock 下面就是闹钟的应用模块
Alarm 调用流程,alarm的流程实现了从上层应用一直到下面driver的调用流程,下面简单阐述: 涉及代码; ./packages/apps/DeskClock/src/com/android/deskclock/Alarms.java ./frameworks/base/core/java/android/app/AlarmManager.java ./frameworks/base/services/java/com/android/server/AlarmManagerService.java ./frameworks/base/services/jni/com_android_server_AlarmManagerService.cpp ./kernel/kernel/drivers/rtc/alarm-dev.c ./kernel/kernel/include/linux/android_alarm.h ./kernel/kernel/drivers/rtc/alarm.c ./kernel/kernel/drivers/rtc/interface.c ./kernel/kernel/drivers/rtc/rtc-pcf8563.c ./packages/apps/DeskClock/src/com/android/deskclock/AlarmReceiver.java ./kernel/arch/arm/configs/mmp2_android_defconfig ./kernel/kernel/kernel/.config 点击Clock 应用程序,然后设置新闹钟,会调到 Alarms.java 里面的 public static long setAlarm(Context context, Alarm alarm) { .... setNextAlert(context); .... } 然后这里面也会调用到 public static void setNextAlert(final Context context) { if (!enableSnoozeAlert(context)) { Alarm alarm = calculateNextAlert(context); //new 一个新的alarm if (alarm != null) { enableAlert(context, alarm, alarm.time); } else { disableAlert(context); } } } 然后继续调用到 private static void enableAlert(Context context, final Alarm alarm, final long atTimeInMillis) { ....... am.set(AlarmManager.RTC_WAKEUP, atTimeInMillis, sender); //这里是RTC_WAKEUP, 这就保证了即使系统睡眠了,都能唤醒,闹钟工作(android平台关机闹钟好像不行) ..... } 然后就调用到了AlarmManager.java 里面方法 public void set(int type, long triggerAtTime, PendingIntent operation) { try { mService.set(type, triggerAtTime, operation); } catch (RemoteException ex) { } } 然后就调用到了AlarmManagerService.java 里面方法 public void set(int type, long triggerAtTime, PendingIntent operation) { setRepeating(type, triggerAtTime, 0, operation); } 然后继续调用 public void setRepeating(int type, long triggerAtTime, long interval, PendingIntent operation) { ..... synchronized (mLock) { Alarm alarm = new Alarm(); alarm.type = type; alarm.when = triggerAtTime; alarm.repeatInterval = interval; alarm.operation = operation; // Remove this alarm if already scheduled. removeLocked(operation); if (localLOGV) Slog.v(TAG, "set: " + alarm); int index = addAlarmLocked(alarm); if (index == 0) { setLocked(alarm); } } } 然后就调用到 private void setLocked(Alarm alarm) { ...... set(mDescriptor, alarm.type, alarmSeconds, alarmNanoseconds); //mDescriptor 这里的文件是 /dev/alarm ..... } 这里就调用到jni了 private native void set(int fd, int type, long seconds, long nanoseconds); 这就调用到了com_android_server_AlarmManagerService.cpp 里面 static JNINativeMethod sMethods[] = { /* name, signature, funcPtr */ {"init", "()I", (void*)android_server_AlarmManagerService_init}, {"close", "(I)V", (void*)android_server_AlarmManagerService_close}, {"set", "(IIJJ)V", (void*)android_server_AlarmManagerService_set}, {"waitForAlarm", "(I)I", (void*)android_server_AlarmManagerService_waitForAlarm}, {"setKernelTimezone", "(II)I", (void*)android_server_AlarmManagerService_setKernelTimezone}, }; set 对应的是android_server_AlarmManagerService_set, 具体是 static void android_server_AlarmManagerService_set(JNIEnv* env, jobject obj, jint fd, jint type, jlong seconds, jlong nanoseconds) { #if HAVE_ANDROID_OS struct timespec ts; ts.tv_sec = seconds; ts.tv_nsec = nanoseconds; int result = ioctl(fd, ANDROID_ALARM_SET(type), &ts); if (result < 0) { LOGE("Unable to set alarm to %lld.%09lld: %s\n", seconds, nanoseconds, strerror(errno)); } #endif } 然后ioctl 就调用到了alarm-dev.c static long alarm_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { .... case ANDROID_ALARM_SET(0): if (copy_from_user(&new_alarm_time, (void __user *)arg, sizeof(new_alarm_time))) { rv = -EFAULT; goto err1; } from_old_alarm_set: spin_lock_irqsave(&alarm_slock, flags); pr_alarm(IO, "alarm %d set %ld.%09ld\n", alarm_type, new_alarm_time.tv_sec, new_alarm_time.tv_nsec); alarm_enabled |= alarm_type_mask; alarm_start_range(&alarms[alarm_type], timespec_to_ktime(new_alarm_time), timespec_to_ktime(new_alarm_time)); spin_unlock_irqrestore(&alarm_slock, flags); if (ANDROID_ALARM_BASE_CMD(cmd) != ANDROID_ALARM_SET_AND_WAIT(0) && cmd != ANDROID_ALARM_SET_AND_WAIT_OLD) break; /* fall though */ .... case ANDROID_ALARM_SET_RTC: if (copy_from_user(&new_rtc_time, (void __user *)arg, sizeof(new_rtc_time))) { rv = -EFAULT; goto err1; } rv = alarm_set_rtc(new_rtc_time); spin_lock_irqsave(&alarm_slock, flags); alarm_pending |= ANDROID_ALARM_TIME_CHANGE_MASK; wake_up(&alarm_wait_queue); spin_unlock_irqrestore(&alarm_slock, flags); if (rv < 0) goto err1; break; .... } 然后这边就调用到了alarm_start_range 设置闹钟, alarm_set_rtc 设置RTC 这两个函数在 android_alarm.h 声明,在 alarm.c 里实现 这是android_alarm.h 里面的声明 void alarm_start_range(struct alarm *alarm, ktime_t start, ktime_t end); int alarm_try_to_cancel(struct alarm *alarm); int alarm_cancel(struct alarm *alarm); ktime_t alarm_get_elapsed_realtime(void); /* set rtc while preserving elapsed realtime */ int alarm_set_rtc(const struct timespec ts); 下面看alarm.c 里面实现: int alarm_set_rtc(struct timespec new_time) { .... ret = rtc_set_time(alarm_rtc_dev, &rtc_new_rtc_time); .... } alarm.c 里面实现了 alarm_suspend alarm_resume 函数 就是如果系统没有suspend的时候,设置闹钟并不会往rtc 芯片的寄存器上写数据,因为不需要唤醒系统,所以闹钟数据时间什么的就通过上层写到设备文件/dev/alarm 里面就可以了,AlarmThread 会不停的去轮寻下一个时间有没有闹钟,直接从设备文件 /dev/alarm 里面读取 第二种,系统要是进入susupend的话,alarm 的alarm_suspend 就会写到下层的rtc芯片的寄存器上去, 然后即使系统suspend之后,闹钟通过rtc 也能唤醒系统 这里就调用到了interface.c 里面 //这里面 int rtc_set_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alarm) 差不多 也是跟下面一样 int rtc_set_time(struct rtc_device *rtc, struct rtc_time *tm) { .... err = rtc->ops->set_time(rtc->dev.parent, tm); .... } 然后set_time 就看到具体的是那个RTC芯片,这边我们是rtc-pcf8563.c static const struct rtc_class_ops pcf8563_rtc_ops = { .read_time = pcf8563_rtc_read_time, .set_time = pcf8563_rtc_set_time, .read_alarm = pcf8563_rtc_read_alarm, .set_alarm = pcf8563_rtc_set_alarm, }; 然后就到了 static int pcf8563_rtc_set_time(struct device *dev, struct rtc_time *tm) { unsigned char buf[TIME_NUM]; int ret; ret = data_calc(buf, tm, TIME_NUM); if (ret < 0) goto out; ret = i2c_smbus_write_i2c_block_data(pcf8563_info->client, PCF8563_RTC_SEC, TIME_NUM, buf); //这边就调用i2c统一接口,往pcf8563rtc芯片寄存器里面写出数据 out: return ret; } 到此,闹钟时间就已经写到rtc 芯片的寄存器里面,第二个参数就是寄存器的名字,后面的buf就是要写入的时间,rtc芯片是额外供电的,所以系统suspend之后,系统kernel都关了,但是rtc里面还有电,寄存器里面数据还是有的(掉电就会丢失数据),所以闹钟到了,通过硬件中断机制就可以唤醒系统。 上面那个rtc下面有几十个rtc芯片驱动代码,没有结构基本一样,都有基本操作函数,注册函数,都是对各自芯片上特有的寄存器操作,为什么调用的是pcf8563rtc呢?这个要看你系统用的是那个芯片,这个可以通过./kernel/kernel/kernel/.config 查看,这边的pcf8563rtc 是当前系统正在使用的芯片型号 # CONFIG_RTC_DRV_ISL1208 is not set # CONFIG_RTC_DRV_X1205 is not set CONFIG_RTC_DRV_PCF8563=y # CONFIG_RTC_DRV_PCF8583 is not set # CONFIG_RTC_DRV_M41T80 is not set 下面是系统唤醒之后,闹钟怎么工作的流程,简单阐述 系统没有suspend的话直接走下面流程,如果suspend的话会被RTC唤醒,然后还是走下面的流程 private class AlarmThread extends Thread { public AlarmThread() { super("AlarmManager"); } public void run() { while (true) { int result = waitForAlarm(mDescriptor); //这里调用jni调用static jint android_server_AlarmManagerService_waitForAlarm,主要还是对 /dev/alarm 操作 .... Alarm alarm = it.next(); try { if (localLOGV) Slog.v(TAG, "sending alarm " + alarm); alarm.operation.send(mContext, 0, mBackgroundIntent.putExtra( Intent.EXTRA_ALARM_COUNT, alarm.count), mResultReceiver, mHandler); .... } } } static jint android_server_AlarmManagerService_waitForAlarm(JNIEnv* env, jobject obj, jint fd) { #if HAVE_ANDROID_OS int result = 0; do { result = ioctl(fd, ANDROID_ALARM_WAIT); } while (result < 0 && errno == EINTR); if (result < 0) { LOGE("Unable to wait on alarm: %s\n", strerror(errno)); return 0; } return result; #endif } AlarmManagerService 里面有个AlarmThread 会一直轮询 /dev/alarm文件,如果打开失败就直接返回,成功就会做一些动作,比如查找时间最近的 alarm,比如睡眠被闹钟唤醒的时候,这边就发一个intent出去,然后在AlarmReceiver.java里面弹出里面会收到就会调用下面的 context.startActivity(alarmAlert); 然后弹出alarm 这个界面 Class c = AlarmAlert.class; 其中public class AlarmAlert extends AlarmAlertFullScreen 所以系统睡眠之后被alarm唤醒弹出的alarm就是这边start的 public class AlarmReceiver extends BroadcastReceiver { /** If the alarm is older than STALE_WINDOW, ignore. It is probably the result of a time or timezone change */ private final static int STALE_WINDOW = 30 * 60 * 1000; @Override public void onReceive(Context context, Intent intent) { ......... Intent alarmAlert = new Intent(context, c); alarmAlert.putExtra(Alarms.ALARM_INTENT_EXTRA, alarm); alarmAlert.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_NO_USER_ACTION); context.startActivity(alarmAlert); ........ } 到这里alarm 就显示出来了 转载地址:http://ogfsi.baihongyu.com/