zoukankan      html  css  js  c++  java
  • 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]
    ", 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
    ");
    
        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
    ", (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/

  • 相关阅读:
    出差公干,人在北京,欢迎骚扰
    义乌江东货运市场托运指南
    哥伦比亚拖鞋批发,60双起批发仅售11,现货3240双,义乌给力商务有限公司诚意供货,发韵达或指定物流
    员工心态 老板心态 学生心态
    淘宝卖家
    开源问题系统
    当一回"青年导师"
    义乌工商学院到给力百货有多远,怎么走?
    我也八卦,姚晨和它男人离婚的事,意料之外,情理之中,吼吼,我是在迅雷看看的评论中看到的
    新浪车模程序写得很有意思
  • 原文地址:https://www.cnblogs.com/hellokitty2/p/11748262.html
Copyright © 2011-2022 走看看