`
sharp2wing
  • 浏览: 271474 次
  • 性别: Icon_minigender_1
  • 来自: 南京
文章分类
社区版块
存档分类
最新评论

从Alarm看Android上层UI到内核代码的流程分析

阅读更多
Alarm 调用流程,alarm的流程实现了从上层应用一直到下面driver的调用流程,下面简单阐述:

AlarmManager里的闹铃类型:

public static final int RTC_WAKEUP = 0;
//当系统进入睡眠状态时,这种类型闹铃会唤醒系统,该闹铃所用时间是绝对时间,是UTC时间
public static final int RTC = 1;
//当系统进入睡眠状态时,这种类型闹铃不会唤醒系统,直到系统下次被唤醒才传递它,该闹铃所用时间是绝对时间,是UTC时间
public static final int ELAPSED_REALTIME_WAKEUP = 2;
//当系统进入睡眠状态时,这种类型闹铃会唤醒系统,该闹铃所用时间是相对时间,是从系统启动后开始计时的,包括睡眠时间
public static final int ELAPSED_REALTIME = 3;
//当系统进入睡眠状态时,这种类型闹铃不会唤醒系统,直到系统下次被唤醒才传递它,该闹铃所用时间是相对时间,是从系统启动后开始计时的,包括睡眠时间
public static final int POWER_OFF_WAKEUP = 5; 
//能唤醒系统,它是一种关机闹铃,就是在关机状态下也可以唤醒系统。使用同RTC类型


涉及代码;
./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 就显示出来了
分享到:
评论

相关推荐

    Android alarm流程从上层到内核的完整分析

    Android alarm流程从上层到内核的完整分析,很完整,代码分析很全

    Android从上层到底层完整流程

    Android 从上层到底层完整流程是指从用户界面到操作系统内核的完整调用流程。这个流程涉及到多个组件,包括应用程序、框架层、内核层等。本文将通过 Alarm 的调用流程来详细解释这个完整流程。 第一步:应用程序层 ...

    Android闹钟内核驱动——Alarm.pdf

    ### Android闹钟内核驱动——Alarm.pdf 知识点总结 #### 1. 基本概念 **Android 内核驱动之 Alarm 驱动** 是 Android 系统内部针对定时器和唤醒机制的一项重要功能。其核心作用在于允许设备即使在睡眠模式下也能...

    Android内核和驱动篇-Android内核介绍

    7. Android Alarm:提供了一个定时器用于把设备从睡眠状态唤醒,同时它也提供了一个即使在设备睡眠时也会运行的时钟基准。源代码位于 drivers/rtc/alarm.c Alarm 驱动程序提供了一个灵活的定时器机制,允许 Android...

    Android内核和驱动篇-Android内核介绍[归纳].pdf

    7. Android Alarm:提供了一个定时器用于把设备从睡眠状态唤醒,同时它也提供了一个即使在设备睡眠时也会运行的时钟基准。源代码位于 drivers/rtc/alarm.c。 Alarm 是 Android 中的一种定时器机制,用于唤醒设备和...

    Android 内核分析报告2

    ### Android 内核分析报告2:Alarm 模块深度解析 #### 一、基本原理 在深入探讨Android内核中的Alarm模块之前,我们先来了解一下它的基本原理。 **Alarm闹钟**是在Android系统中基于标准RTC(实时时钟)驱动之上...

    Android内核和驱动篇-Android内核介绍.txt

    根据给定文件的信息,我们可以深入探讨Android内核与驱动程序的关键知识点,这将为我们提供一个全面的视角,了解Android操作系统底层技术的复杂性和创新性。 ### Android内核基础 Android内核是基于Linux内核的一...

    文档-Android Alarm结构分析 + Linux高精度时钟分析

    在Android系统中,Alarm服务是核心组件之一,用于在特定时间...通过阅读"Android闹钟内核驱动——Alarm.pdf"和"Linux_高精度时钟分析.txt",我们可以深入学习这些知识,并掌握在实际开发中有效利用Alarm机制的方法。

    Android底层开发技术实战详解--内核、移植和驱动.(电子工业.王振丽).part3

    4.4.3 分析sensor源代码看Android api 与硬件平台的衔接 104 4.5 移植总结 116 4.5.1 移植各个Android部件的方式 116 4.5.2 移植技巧之一——不得不说的辅助工作 117 第5章 goldfish下的驱动解析 ...

    android2.1内置闹钟源码AlarmClock

    总之,分析`AlarmClock`的源码能够帮助我们深入理解Android系统的事件调度机制,提升我们的应用开发技能,特别是涉及到时间管理和系统级服务交互的场景。同时,也可以学习到早期Android版本的设计理念和实践,对比...

    Android内核驱动原理.pdf

    综合来看,Android内核驱动原理涉及到的内容非常广泛,它不仅需要理解Linux内核的基础,还需要深入掌握Android系统的各个内核组件,包括进程间通信、内存管理、设备驱动、USB设备支持、日志系统、以及特定的调试工具...

    android.AlarmClock

    《Android AlarmClock程序详解与实践》 在Android操作系统中,`AlarmClock`是系统提供的一项重要功能,它允许开发者创建应用程序来管理和设置闹钟。在本文中,我们将深入探讨`AlarmClock`的工作原理,以及如何在...

    android alarmclock

    android alarmclock 源代码

    AlarmClock源码

    此外,源码中还可能涉及线程管理,如Handler和Looper的使用,以确保UI操作在主线程中进行,而耗时操作则在工作线程中执行,遵循Android的UI线程规则。 总的来说,AlarmClock源码是一个深入了解Android系统服务、...

    alarm_android_

    在Android平台上,开发一款具有自定义备忘录功能的应用,涉及到多个关键的技术点。这个"alarm_android_"项目可能就是这样一个应用,它允许用户创建个性化备忘录,设定特定时间,并自定义提醒闹钟的属性,如类型、...

    Android内核和驱动篇 Android内核介绍.doc

    Android Logger是一个轻量级的日志系统,用于收集和记录Android系统的各种日志信息,源代码位于`drivers/staging/android/logger.c`,为故障排查和性能分析提供了便利。 Android Alarm驱动则是一个定时器,能够唤醒...

    android AlarmClock

    《Android AlarmClock 源码解析与应用》 在Android操作系统中,AlarmClock是一个至关重要的组件,它允许开发者创建和管理应用程序的定时提醒功能。本文将深入探讨Android AlarmClock的源码,理解其工作原理,并指导...

    Android中的Alarm

    在Android系统中,`Alarm`是一个非常重要的组件,它允许应用程序在未来的某个时间点执行一个操作,例如启动服务、发送广播或执行其他任务。`Alarm`是Android中的定时任务服务,开发者可以设置一次性或者周期性的任务...

Global site tag (gtag.js) - Google Analytics