alarm学习笔记

 

一、alarm的调用过程

1. alarm设置过程

frameworks/base/core/java/android/app/AlarmManager.java

public void set(@AlarmType int type, long triggerAtMillis, PendingIntent operation) {
    setImpl(type, triggerAtMillis, legacyExactLength(), 0, 0, operation, null, null, null, null, null);
}

private void setImpl(@AlarmType int type, long triggerAtMillis, long windowMillis,long intervalMillis, int flags, PendingIntent operation, final OnAlarmListener listener,String listenerTag,
    Handler targetHandler, WorkSource workSource,AlarmClockInfo alarmClock) {
            mService.set(mPackageName, type, triggerAtMillis, windowMillis, intervalMillis, flags, operation, recipientWrapper, listenerTag, workSource, alarmClock);
}


frameworks/base/services/core/java/com/android/server/AlarmManagerService.java

private final IBinder mService = new IAlarmManager.Stub() {
    @Override
    public void set(String callingPackage,
        int type, long triggerAtTime, long windowLength, long interval, int flags,
        PendingIntent operation, IAlarmListener directReceiver, String listenerTag,
        WorkSource workSource, AlarmManager.AlarmClockInfo alarmClock) {
            setImpl(type, triggerAtTime, windowLength, interval, operation, directReceiver, listenerTag, flags, workSource, alarmClock, callingUid, callingPackage);
        }
}

void setImpl(int type, long triggerAtTime, long windowLength, long interval,
    PendingIntent operation, IAlarmListener directReceiver, String listenerTag,
    int flags, WorkSource workSource, AlarmManager.AlarmClockInfo alarmClock,
    int callingUid, String callingPackage) {
        setImplLocked(type, triggerAtTime, triggerElapsed, windowLength, maxElapsed,
            interval, operation, directReceiver, listenerTag, flags, true, workSource,
            alarmClock, callingUid, callingPackage);
    }
 }

private void setImplLocked(int type, long when, long whenElapsed, long windowLength,
    long maxWhen, long interval, PendingIntent operation, IAlarmListener directReceiver,
    String listenerTag, int flags, boolean doValidate, WorkSource workSource,
    AlarmManager.AlarmClockInfo alarmClock, int callingUid, String callingPackage) {
        Alarm a = new Alarm(type, when, whenElapsed, windowLength, maxWhen, interval,
            operation, directReceiver, listenerTag, workSource, flags, alarmClock,
            callingUid, callingPackage);
        setImplLocked(a, false, doValidate);
    }
}

private void setImplLocked(Alarm a, boolean rebatching, boolean doValidate) {
    rescheduleKernelAlarmsLocked();
}

void rescheduleKernelAlarmsLocked() {
    setLocked(ELAPSED_REALTIME, nextNonWakeup);
}

private void setLocked(int type, long when) {
    final int result = set(mNativeData, type, alarmSeconds, alarmNanoseconds);
}

private native int set(long nativeData, int type, long seconds, long nanoseconds);

frameworks/base/services/core/jni/com_android_server_AlarmManagerService.cpp

static const JNINativeMethod sMethods[] = {
    {"set", "(JIJJ)I", (void*)android_server_AlarmManagerService_set},
}
 
static jint android_server_AlarmManagerService_set(JNIEnv*, jobject, jlong nativeData, jint type, jlong seconds, jlong nanoseconds)
{
    const int result = impl->set(type, &ts);
}
 
int AlarmImpl::set(int type, struct timespec *ts)
{
    return timerfd_settime(fds[type], TFD_TIMER_ABSTIME, &spec, NULL);
}

通过系统调用设置内核时间:

SYSCALL_DEFINE4(timerfd_settime, int, ufd, int, flags, const struct itimerspec __user *, utmr, struct itimerspec __user *, otmr)

 

2. Android上层通过poll一直在监听ararm,接收到通知后,将相应的事件进行分发。

public class AlarmManager {
    public static final int RTC_WAKEUP = 0;
    public static final int RTC = 1;
    public static final int ELAPSED_REALTIME_WAKEUP = 2;
    public static final int ELAPSED_REALTIME = 3;
}

private class AlarmThread extends Thread
{
    public AlarmThread()
    {
        super("AlarmManager");
    }

    public void run()
    {
        ArrayList<Alarm> triggerList = new ArrayList<Alarm>();

        while (true)
        {
            int result = waitForAlarm(mNativeData);
            deliverAlarmsLocked();
        }
    }
 }

只有type类型为0或者2的alarm客户唤醒系统。可以根据打印信息查找唤醒系统的闹钟,如

09-19 15:00:05.968   813   952 D AlarmManager: sending alarm.type = 2, action = com.android.providers.calendar.intent.CalendarProvider2, cn = ComponentInfo{com.android.providers.calendar/com.android.providers.calendar.CalendarProviderBroadcastReceiver}, operation = PendingIntent{3eaf5c8: PendingIntentRecord{29dc661 com.android.providers.calendar broadcastIntent}}
09-19 15:01:46.941   813   952 D AlarmManager: sending alarm.type = 0, action = null, cn = ComponentInfo{cn.showmac.vsimservice/cn.jpush.android.service.AlarmReceiver}, operation = PendingIntent{ec569d7: PendingIntentRecord{c33696d cn.showmac.vsimservice broadcastIntent}}

 

二、系统调用函数timerfd函数集

1. man timerfd_create

NAME
       timerfd_create, timerfd_settime, timerfd_gettime - timers that notify via file descriptors

SYNOPSIS
       #include <sys/timerfd.h>

       int timerfd_create(int clockid, int flags);

       int timerfd_settime(int fd, int flags, const struct itimerspec *new_value, struct itimerspec *old_value);

       int timerfd_gettime(int fd, struct itimerspec *curr_value);

2. 使用demo

#include <sys/timerfd.h>
#include <time.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>  /* Definition of uint64_t */

#define handle_error(msg) do { perror(msg); exit(EXIT_FAILURE); } while (0)

static void print_elapsed_time(void)
{
    static struct timespec start;
    static int first_call = 1;
    struct timespec curr;
    int secs, nsecs;

    if (first_call) {
        first_call = 0;
        if (clock_gettime(CLOCK_MONOTONIC, &start) == -1) {
            handle_error("clock_gettime");       
        }
    }

    if (clock_gettime(CLOCK_MONOTONIC, &curr) == -1) {
        handle_error("clock_gettime"); 
    }

    secs = curr.tv_sec - start.tv_sec;
    nsecs = curr.tv_nsec - start.tv_nsec;
    if (nsecs < 0) {
        secs--;
        nsecs += 1000000000;
    }
    printf("%d.%03d: ", secs, (nsecs + 500000) / 1000000);
}

int main(int argc, char *argv[])
{
    struct itimerspec new_value;
    int max_exp, fd;
    struct timespec now;
    uint64_t exp, tot_exp;
    ssize_t s;

    if ((argc != 2) && (argc != 4)) {
       fprintf(stderr, "%s init-secs [interval-secs max-exp]\n", argv[0]);
       exit(EXIT_FAILURE);
    }

    if (clock_gettime(CLOCK_REALTIME, &now) == -1) {
        handle_error("clock_gettime");    
    }

   /* Create a CLOCK_REALTIME absolute timer with initial
      expiration and interval as specified in command line */
   new_value.it_value.tv_sec = now.tv_sec + atoi(argv[1]);
   new_value.it_value.tv_nsec = now.tv_nsec;
   if (argc == 2) {
        new_value.it_interval.tv_sec = 0;
        max_exp = 1;
    } else {
        new_value.it_interval.tv_sec = atoi(argv[2]);
        max_exp = atoi(argv[3]);
    }
    new_value.it_interval.tv_nsec = 0;

    /* begin use these functions */
    fd = timerfd_create(CLOCK_REALTIME, 0);
    if (fd == -1)
        handle_error("timerfd_create");

    if (timerfd_settime(fd, TFD_TIMER_ABSTIME, &new_value, NULL) == -1)
        handle_error("timerfd_settime");

    print_elapsed_time();
    printf("timer started\n");

    for (tot_exp = 0; tot_exp < max_exp;) {
        s = read(fd, &exp, sizeof(uint64_t));
        if (s != sizeof(uint64_t))
            handle_error("read");
        tot_exp += exp;
        print_elapsed_time();
        printf("read: %llu; total=%llu\n", (unsigned long long) exp, (unsigned long long) tot_exp);
    }

    exit(EXIT_SUCCESS);
}

/*
$ ./pp 3 1 100  //第一次定时3s,之后每1秒通知1次。
0.000: timer started
3.002: read: 1; total=1
4.001: read: 1; total=2
5.000: read: 1; total=3
6.001: read: 1; total=4
^Z
[1]+  Stopped                 ./pp 3 1 100
$ fg
./pp 3 1 100
21.808: read: 15; total=19  //Ctrl+Z后app被挂起了,app就收不到通知了。
22.000: read: 1; total=20
23.001: read: 1; total=21
^C
*/

 

纠正:timerfd与alarmtimer没有直接关系,跟踪timerfd_create系统调用它有自己的file_operations结构体(并没有导出设备节点给上层,而是timerfd_create()直接就返回一个fd)

三、RTC时钟

1. alarm底层使用的是rtc定时器

//kernel/time/alarmtimer.c

static const struct dev_pm_ops alarmtimer_pm_ops = {
    .suspend = alarmtimer_suspend,
};
 
static struct platform_driver alarmtimer_driver = {
    .driver = {
        .name = "alarmtimer",
        .pm = &alarmtimer_pm_ops,
    },
};

 

四、Android系统中的一些使用

1. 电池状态更新

static void wakealarm_set_interval(int interval) {
    itval.it_interval.tv_sec = interval;
    itval.it_interval.tv_nsec = 0;
    itval.it_value.tv_sec = interval;
    itval.it_value.tv_nsec = 0;
    timerfd_settime(wakealarm_fd, 0, &itval, NULL);
}

void healthd_battery_update_internal(bool charger_online) {
    // Fast wake interval when on charger (watch for overheat);
    // slow wake interval when on battery (watch for drained battery).
    int new_wake_interval = charger_online ? healthd_config.periodic_chores_interval_fast  //充电时1min rtc唤醒一次
                                        : healthd_config.periodic_chores_interval_slow; //非充电时10min rtc唤醒一次
}

 

五、相关总结

1. AlarmManagerService.mHandler 也是运行在system_server的主线程;
2. 防止alarm频繁发起,则最小时间间隔5s;
3. 设置闹钟有3种类型:

set(int type,long startTime,PendingIntent pi),//设置一次闹钟
setRepeating(int type,long startTime,long intervalTime,PendingIntent pi),//设置重复闹钟
setInexactRepeating(int type,long startTime,long intervalTime,PendingIntent pi),//设置重复闹钟,但不准确

Type有4种类型:

类型是否能唤醒系统是否包含休眠时间
RTC_WAKEUP
RTC
ELAPSED_REALTIME_WAKEUP
ELAPSED_REALTIME

 

 

 

 

 

 

 

 

 

参考:

理解AlarmManager机制: http://gityuan.com/2017/03/12/alarm_manager_service/

 

posted on 2019-10-27 17:36  Hello-World3  阅读(863)  评论(0编辑  收藏  举报

导航