zoukankan      html  css  js  c++  java
  • Android Alarm自上而下 调试浅析

    AlarmManager中的type

    public   static   final   int  ELAPSED_REALTIME
    // 当系统进入睡眠状态时,这种类型的闹铃不会唤醒系统。直到系统下次被唤醒才传递它,该闹铃所用的时间是相对时间,是从系统启动后开始计时的,包括睡眠时 间,可以通过调用SystemClock.elapsedRealtime()获得。系统值是3    (0x00000003)。     

    public   static   final   int  ELAPSED_REALTIME_WAKEUP
    //能唤醒系统,用法同ELAPSED_REALTIME,系统值是2 (0x00000002) 。     

    public   static   final   int  RTC
    //当系统进入睡眠状态时,这种类型的闹铃不会唤醒系统。直到系统下次被唤醒才传递它,该闹铃所用的时间是绝对时间,所用时间是UTC时间,可以通过调用 System.currentTimeMillis()获得。系统值是1 (0x00000001) 。     

    public   static   final   int  RTC_WAKEUP
    //能唤醒系统,用法同RTC类型,系统值为 0 (0x00000000) 。     

    Public static   final   int  POWER_OFF_WAKEUP
    //能唤醒系统,它是一种关机闹铃,就是说设备在关机状态下也可以唤醒系统,所以我们把它称之为关机闹铃。使用方法同RTC类型,系统值为4(0x00000004)。

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

    点击Clock 应用程序,然后设置新闹钟,会调到  Alarms.Java  里面的

        public static long setAlarm(Context context, Alarm alarm) {
            ContentValues values = createContentValues(alarm);
            ContentResolver resolver = context.getContentResolver();
            resolver.update(
                    ContentUris.withAppendedId(Alarm.Columns.CONTENT_URI, alarm.id),
                    values, null, null);
    
            long timeInMillis = calculateAlarm(alarm);
    
            if (alarm.enabled) {
                // Disable the snooze if we just changed the snoozed alarm. This
                // only does work if the snoozed alarm is the same as the given
                // alarm.
                // TODO: disableSnoozeAlert should have a better name.
                disableSnoozeAlert(context, alarm.id);
    
                // Disable the snooze if this alarm fires before the snoozed alarm.
                // This works on every alarm since the user most likely intends to
                // have the modified alarm fire next.
                clearSnoozeIfNeeded(context, timeInMillis);
            }
    
            setNextAlert(context);
    
            return timeInMillis;
        }

    然后这里面也会调用到

        public static void setNextAlert(final Context context) {
            final Alarm alarm = calculateNextAlert(context);
            if (alarm != null) {
                enableAlert(context, alarm, alarm.time);
            } else {
                disableAlert(context);
            }
        }

    calculateNextAlert(context);   //new 一个新的alarm 
    然后继续调用到

         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
    ", seconds, nanoseconds, strerror(errno));
        }
    #endif
    }

    然后ioctl 就调用到了alarm-dev.c

    static long alarm_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
    {
        int rv = 0;
        unsigned long flags;
        struct timespec new_alarm_time;
        struct timespec new_rtc_time;
        struct timespec tmp_time;
        enum android_alarm_type alarm_type = ANDROID_ALARM_IOCTL_TO_TYPE(cmd);
        uint32_t alarm_type_mask = 1U << alarm_type;
        printk(">>%s cmd == %d
    ",__FUNCTION__,cmd);
        if (alarm_type >= ANDROID_ALARM_TYPE_COUNT)
            return -EINVAL;
    
        if (ANDROID_ALARM_BASE_CMD(cmd) != ANDROID_ALARM_GET_TIME(0)) {
            if ((file->f_flags & O_ACCMODE) == O_RDONLY)
                return -EPERM;
            if (file->private_data == NULL &&
                cmd != ANDROID_ALARM_SET_RTC) {
                spin_lock_irqsave(&alarm_slock, flags);
                if (alarm_opened) {
                    spin_unlock_irqrestore(&alarm_slock, flags);
                    return -EBUSY;
                }
                alarm_opened = 1;
                file->private_data = (void *)1;
                spin_unlock_irqrestore(&alarm_slock, flags);
            }
        }
    
        switch (ANDROID_ALARM_BASE_CMD(cmd)) {
        case ANDROID_ALARM_CLEAR(0):
            spin_lock_irqsave(&alarm_slock, flags);
            pr_alarm(IO, "alarm %d clear
    ", alarm_type);
            alarm_try_to_cancel(&alarms[alarm_type]);
            if (alarm_pending) {
                alarm_pending &= ~alarm_type_mask;
                if (!alarm_pending && !wait_pending)
                    wake_unlock(&alarm_wake_lock);
            }
            alarm_enabled &= ~alarm_type_mask;
            spin_unlock_irqrestore(&alarm_slock, flags);
            break;
    
        case ANDROID_ALARM_SET_OLD:
        case ANDROID_ALARM_SET_AND_WAIT_OLD:
            if (get_user(new_alarm_time.tv_sec, (int __user *)arg)) {
                rv = -EFAULT;
                goto err1;
            }
            new_alarm_time.tv_nsec = 0;
            goto from_old_alarm_set;
    
        case ANDROID_ALARM_SET_AND_WAIT(0):
        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
    ", 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_WAIT:
            spin_lock_irqsave(&alarm_slock, flags);
            pr_alarm(IO, "alarm wait
    ");
            if (!alarm_pending && wait_pending) {
                wake_unlock(&alarm_wake_lock);
                wait_pending = 0;
            }
            spin_unlock_irqrestore(&alarm_slock, flags);
            rv = wait_event_interruptible(alarm_wait_queue, alarm_pending);
            if (rv)
                goto err1;
            spin_lock_irqsave(&alarm_slock, flags);
            rv = alarm_pending;
            wait_pending = 1;
            alarm_pending = 0;
            spin_unlock_irqrestore(&alarm_slock, flags);
            break;
        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;
        case ANDROID_ALARM_GET_TIME(0):
            switch (alarm_type) {
            case ANDROID_ALARM_RTC_WAKEUP:
            case ANDROID_ALARM_RTC:
                getnstimeofday(&tmp_time);
                break;
            case ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP:
            case ANDROID_ALARM_ELAPSED_REALTIME:
                tmp_time =
                    ktime_to_timespec(alarm_get_elapsed_realtime());
                break;
            case ANDROID_ALARM_TYPE_COUNT:
            case ANDROID_ALARM_SYSTEMTIME:
                ktime_get_ts(&tmp_time);
                break;
            }
            if (copy_to_user((void __user *)arg, &tmp_time,
                sizeof(tmp_time))) {
                rv = -EFAULT;
                goto err1;
            }
            break;
    
        default:
            rv = -EINVAL;
            goto err1;
        }
    err1:
        return rv;
    }

    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-hym8563.c
    然后就到了

    static int hym8563_i2c_set_regs(struct i2c_client *client, u8 reg, u8 const buf[], __u16 len)
    {
        int ret; 
        ret = i2c_master_reg8_send(client, reg, buf, (int)len, RTC_SPEED);
        return ret;
    }

    到此,闹钟时间就已经写到rtc 芯片的寄存器里面,第二个参数就是寄存器的名字,后面的buf就是要写入的时间,rtc芯片是额外供电的,所以系统suspend之后,系统kernel都关了,但是rtc里面还有电,寄存器里面数据还是有的(掉电就会丢失数据),所以闹钟到了,通过硬件中断机制就可以唤醒系统。

    3.下面是系统唤醒之后,闹钟怎么工作的流程,简单阐述

     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
    ", strerror(errno));
            return 0;
        }
        
        return result;
    #endif
    }

    系统没有suspend的话直接走下面流程,如果suspend的话会被RTC唤醒,然后还是走下面的流程
    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 就显示出来了

  • 相关阅读:
    Spark Streaming企业运用
    spark企业运用
    sparkcore企业运用
    Spark GraphX从入门到实战
    sparkstreaming入门到实战
    sparkSql从入门到实战
    sparkcore入门到实战
    spark机器算法从入门到实战
    flink实时数仓从入门到实战
    github上克隆私有项目
  • 原文地址:https://www.cnblogs.com/chenlong-50954265/p/5437685.html
Copyright © 2011-2022 走看看