生活随笔
收集整理的這篇文章主要介紹了
Android Alarm自上而下 调试浅析
小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
1.為了創(chuàng)建一個(gè)新的Alarm,使用set方法并指定一個(gè)Alarm類型、觸發(fā)時(shí)間和在Alarm觸發(fā)時(shí)要調(diào)用的Intent。如果你設(shè)定的Alarm發(fā)生在過去,那么,它將立即觸發(fā)。
這里有4種Alarm類型。你的選擇將決定你在set方法中傳遞的時(shí)間值代表什么,是特定的時(shí)間或者是時(shí)間流逝:
? RTC_WAKEUP
在指定的時(shí)刻(設(shè)置Alarm的時(shí)候),喚醒設(shè)備來觸發(fā)Intent。
? RTC
在一個(gè)顯式的時(shí)間觸發(fā)Intent,但不喚醒設(shè)備。
? ELAPSED_REALTIME
從設(shè)備啟動后,如果流逝的時(shí)間達(dá)到總時(shí)間,那么觸發(fā)Intent,但不喚醒設(shè)備。流逝的時(shí)間包括設(shè)備睡眠的任何時(shí)間。注意一點(diǎn)的是,時(shí)間流逝的計(jì)算點(diǎn)是自從它最后一次啟動算起。
? ELAPSED_REALTIME_WAKEUP
從設(shè)備啟動后,達(dá)到流逝的總時(shí)間后,如果需要將喚醒設(shè)備并觸發(fā)Intent。
2.Alarm 調(diào)用流程,alarm的流程實(shí)現(xiàn)了從上層應(yīng)用一直到下面driver的調(diào)用流程,下面簡單闡述:
點(diǎn)擊Clock 應(yīng)用程序,然后設(shè)置新鬧鐘,會調(diào)到? Alarms.java? 里面的
[cpp]?view plaincopy
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)?{?? ?????????? ?????????? ?????????? ?????????? ????????disableSnoozeAlert(context,?alarm.id);?? ?? ?????????? ?????????? ?????????? ????????clearSnoozeIfNeeded(context,?timeInMillis);?? ????}?? ?? ????setNextAlert(context);?? ?? ????return?timeInMillis;?? }??
然后這里面也會調(diào)用到
[cpp]?view plaincopy
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 一個(gè)新的alarm?
然后繼續(xù)調(diào)用到
?? ? private static void enableAlert(Context context, final Alarm alarm,final long atTimeInMillis)
其中am.set(AlarmManager.RTC_WAKEUP, atTimeInMillis, sender);//這里是RTC_WAKEUP, 這就保證了即使系統(tǒng)睡眠了,都能喚醒,鬧鐘工作(android平臺關(guān)機(jī)鬧鐘好像不行)
然后就調(diào)用到了AlarmManager.java 里面方法
[cpp]?view plaincopy
public?void?set(int?type,?long?triggerAtTime,?PendingIntent?operation)?{?? ????try?{?? ????????mService.set(type,?triggerAtTime,?operation);?? ????}?catch?(RemoteException?ex)?{?? ????}?? }??
然后就調(diào)用到了AlarmManagerService.java? 里面方法
[cpp]?view plaincopy
public?void?set(int?type,?long?triggerAtTime,?PendingIntent?operation)?{?? ????????setRepeating(type,?triggerAtTime,?0,?operation);?? ????}??
然后繼續(xù)調(diào)用
[cpp]?view plaincopy
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;?? ?? ?????????????? ????????????removeLocked(operation);?? ?? ????????????if?(localLOGV)?Slog.v(TAG,?"set:?"?+?alarm);?? ?? ????????????int?index?=?addAlarmLocked(alarm);?? ????????????if?(index?==?0)?{?? ????????????????setLocked(alarm);?? ????????????}?? ????????}?? ????}??
然后就調(diào)用到
[cpp]?view plaincopy
private?void?setLocked(Alarm?alarm)?? ????{?? ????......?? ????set(mDescriptor,?alarm.type,?alarmSeconds,?alarmNanoseconds);????? ????.....?? }??
這里就調(diào)用到j(luò)ni了
private native void set(int fd, int type, long seconds, long nanoseconds);
這就調(diào)用到了com_android_server_AlarmManagerService.cpp 里面
[cpp]?view plaincopy
static?JNINativeMethod?sMethods[]?=?{?? ??????? ????{"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 對應(yīng)的是android_server_AlarmManagerService_set, 具體是
[cpp]?view plaincopy
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 就調(diào)用到了alarm-dev.c
[cpp]?view plaincopy
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\n",__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\n",?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\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;?? ?????????? ????case?ANDROID_ALARM_WAIT:?? ????????spin_lock_irqsave(&alarm_slock,?flags);?? ????????pr_alarm(IO,?"alarm?wait\n");?? ????????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? 里面實(shí)現(xiàn)了 alarm_suspend? alarm_resume 函數(shù)
就是如果系統(tǒng)沒有suspend的時(shí)候,設(shè)置鬧鐘并不會往rtc 芯片的寄存器上寫數(shù)據(jù),因?yàn)椴恍枰獑拘严到y(tǒng),所以鬧鐘數(shù)據(jù)時(shí)間什么的就通過上層寫到設(shè)備文件/dev/alarm
里面就可以了,AlarmThread 會不停的去輪尋下一個(gè)時(shí)間有沒有鬧鐘,直接從設(shè)備文件 /dev/alarm 里面讀取
第二種,系統(tǒng)要是進(jìn)入susupend的話,alarm 的alarm_suspend? 就會寫到下層的rtc芯片的寄存器上去, 然后即使系統(tǒng)suspend之后,鬧鐘通過rtc 也能喚醒系統(tǒng)。
這里就調(diào)用到了interface.c 里面?? //這里面 int rtc_set_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alarm) 差不多 也是跟下面一樣
[cpp]?view plaincopy
int?rtc_set_time(struct?rtc_device?*rtc,?struct?rtc_time?*tm)?? {?? ....?? ????err?=?rtc->ops->set_time(rtc->dev.parent,?tm);?? ....?? }??
然后set_time 就看到具體的是那個(gè)RTC芯片,這邊我們是rtc-hym8563.c
然后就到了
[cpp]?view plaincopy
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;?? }??
到此,鬧鐘時(shí)間就已經(jīng)寫到rtc 芯片的寄存器里面,第二個(gè)參數(shù)就是寄存器的名字,后面的buf就是要寫入的時(shí)間,rtc芯片是額外供電的,所以系統(tǒng)suspend之后,系統(tǒng)kernel都關(guān)了,但是rtc里面還有電,寄存器里面數(shù)據(jù)還是有的(掉電就會丟失數(shù)據(jù)),所以鬧鐘到了,通過硬件中斷機(jī)制就可以喚醒系統(tǒng)。
3.下面是系統(tǒng)喚醒之后,鬧鐘怎么工作的流程,簡單闡述
[cpp]?view plaincopy
?private?class?AlarmThread?extends?Thread?? ????{?? ????????public?AlarmThread()?? ????????{?? ????????????super("AlarmManager");?? ????????}?? ?????????? ????????public?void?run()?? ????????{??? ????????while?(true)?? ????????????{?? ????????int?result?=?waitForAlarm(mDescriptor);??? ????????....?? ????????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?? }??
系統(tǒng)沒有suspend的話直接走下面流程,如果suspend的話會被RTC喚醒,然后還是走下面的流程
AlarmManagerService? 里面有個(gè)AlarmThread? 會一直輪詢 /dev/alarm文件,如果打開失敗就直接返回,成功就會做一些動作,比如查找時(shí)間最近的
alarm,比如睡眠被鬧鐘喚醒的時(shí)候,這邊就發(fā)一個(gè)intent出去,然后在AlarmReceiver.java里面彈出里面會收到就會調(diào)用下面的
?? ??? ?context.startActivity(alarmAlert);
然后彈出alarm? 這個(gè)界面
??????? Class c = AlarmAlert.class;
其中public class AlarmAlert extends AlarmAlertFullScreen? 所以系統(tǒng)睡眠之后被alarm喚醒彈出的alarm就是這邊start的
[cpp]?view plaincopy
public?class?AlarmReceiver?extends?BroadcastReceiver?{?? ?? ????? ?? ????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 就顯示出來了
總結(jié)
以上是生活随笔為你收集整理的Android Alarm自上而下 调试浅析的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。