Android Alarm机制分析
从应用层到内核层,简单分析Android Alarm的工作流程。基于Android 4.4和Kernel 2.6.39。
应用层
/packages/apps/DeskClock/目录下为Android的系统闹钟APP,可参考其闹钟实现。
在/packages/apps/DeskClock/src/com/android/deskclock/AlarmClockFragment.java:
mAddAlarmButton = (ImageButton) v.findViewById(R.id.alarm_add_alarm);mAddAlarmButton.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {hideUndoBar(true, null);startCreatingAlarm();}});
给按钮绑定监听事件,调用startCreatingAlarm()函数
private void startCreatingAlarm() {// Set the "selected" alarm as null, and we'll create the new one when the timepicker// comes back.mSelectedAlarm = null;AlarmUtils.showTimeEditDialog(getChildFragmentManager(),null, AlarmClockFragment.this, DateFormat.is24HourFormat(getActivity()));}
/packages/apps/DeskClock/src/com/android/deskclock/AlarmUtils.java:
public static void showTimeEditDialog(FragmentManager manager, final Alarm alarm,TimePickerDialog.OnTimeSetListener listener, boolean is24HourMode) {...TimePickerDialog dialog = TimePickerDialog.newInstance(listener,hour, minutes, is24HourMode);dialog.setThemeDark(true);// Make sure the dialog isn't already added.manager.executePendingTransactions();final FragmentTransaction ft = manager.beginTransaction();final Fragment prev = manager.findFragmentByTag(FRAG_TAG_TIME_PICKER);if (prev != null) {ft.remove(prev);}ft.commit();if (dialog != null && !dialog.isAdded()) {dialog.show(manager, FRAG_TAG_TIME_PICKER);}}
打开了一个时间选择的Dialog,dialog事件监听器是由AlarmClockFragment传入的TimePickerDialog.OnTimeSetListener,回调函数为onTimeSet。
再回到AlarmClockFragment.java中:
/*** AlarmClock application.*/
public class AlarmClockFragment extends DeskClockFragment implementsLoaderManager.LoaderCallbacks<Cursor>,TimePickerDialog.OnTimeSetListener,View.OnTouchListener
{...// Callback used by TimePickerDialog@Overridepublic void onTimeSet(RadialPickerLayout view, int hourOfDay, int minute) {if (mSelectedAlarm == null) {// If mSelectedAlarm is null then we're creating a new alarm.Alarm a = new Alarm();a.alert = RingtoneManager.getActualDefaultRingtoneUri(getActivity(),RingtoneManager.TYPE_ALARM);if (a.alert == null) {a.alert = Uri.parse("content://settings/system/alarm_alert");}a.hour = hourOfDay;a.minutes = minute;a.enabled = true;asyncAddAlarm(a);} else {mSelectedAlarm.hour = hourOfDay;mSelectedAlarm.minutes = minute;mSelectedAlarm.enabled = true;mScrollToAlarmId = mSelectedAlarm.id;asyncUpdateAlarm(mSelectedAlarm, true);mSelectedAlarm = null;}}...
}
在onTimeSet函数中 asyncAddAlarm(a);
private void asyncAddAlarm(final Alarm alarm) {final Context context = AlarmClockFragment.this.getActivity().getApplicationContext();final AsyncTask<Void, Void, AlarmInstance> updateTask =new AsyncTask<Void, Void, AlarmInstance>() {@Overridepublic synchronized void onPreExecute() {...}@Overrideprotected AlarmInstance doInBackground(Void... parameters) {if (context != null && alarm != null) {ContentResolver cr = context.getContentResolver();// Add alarm to dbAlarm newAlarm = Alarm.addAlarm(cr, alarm);mScrollToAlarmId = newAlarm.id;// Create and add instance to dbif (newAlarm.enabled) {return setupAlarmInstance(context, newAlarm);}}return null;}@Overrideprotected void onPostExecute(AlarmInstance instance) {...}};updateTask.execute();}
asyncAddAlarm函数中是一个AsyncTask后台任务,主要关系doInBackground函数。
通过Content Provider在数据库添加了一个数据。
然后调用了
private static AlarmInstance setupAlarmInstance(Context context, Alarm alarm) {ContentResolver cr = context.getContentResolver();AlarmInstance newInstance = alarm.createInstanceAfter(Calendar.getInstance());newInstance = AlarmInstance.addInstance(cr, newInstance);// Register instance to state managerAlarmStateManager.registerInstance(context, newInstance, true);return newInstance;}
在/packages/apps/DeskClock/src/com/android/deskclock/provider/Alarm.java中:
public static Alarm addAlarm(ContentResolver contentResolver, Alarm alarm) {ContentValues values = createContentValues(alarm);Uri uri = contentResolver.insert(CONTENT_URI, values);alarm.id = getId(uri);return alarm;}
在setupAlarmInstance中,在AlarmStateManager注册了一个实例:
// Register instance to state managerAlarmStateManager.registerInstance(context, newInstance, true);
然后我们来查看:
/packages/apps/DeskClock/src/com/android/deskclock/alarms/AlarmStateManager.java
/*** This registers the AlarmInstance to the state manager. This will look at the instance* and choose the most appropriate state to put it in. This is primarily used by new* alarms, but it can also be called when the system time changes.** Most state changes are handled by the states themselves, but during major time changes we* have to correct the alarm instance state. This means we have to handle special cases as* describe below:** <ul>* <li>Make sure all dismissed alarms are never re-activated</li>* <li>Make sure firing alarms stayed fired unless they should be auto-silenced</li>* <li>Missed instance that have parents should be re-enabled if we went back in time</li>* <li>If alarm was SNOOZED, then show the notification but don't update time</li>* <li>If low priority notification was hidden, then make sure it stays hidden</li>* </ul>** If none of these special case are found, then we just check the time and see what is the* proper state for the instance.** @param context application context* @param instance to register*/public static void registerInstance(Context context, AlarmInstance instance,boolean updateNextAlarm) {...// Fix states that are time sensitiveif (currentTime.after(missedTTL)) {// Alarm is so old, just dismiss itsetDismissState(context, instance);} else if (currentTime.after(alarmTime)) {setMissedState(context, instance);} else if (instance.mAlarmState == AlarmInstance.SNOOZE_STATE) {// We only want to display snooze notification and not update the time,// so handle showing the notification directlyAlarmNotifications.showSnoozeNotification(context, instance);scheduleInstanceStateChange(context, instance.getAlarmTime(),instance, AlarmInstance.FIRED_STATE);}...}
在scheduleInstanceStateChange函数中:
private static void scheduleInstanceStateChange(Context context, Calendar time,AlarmInstance instance, int newState) {long timeInMillis = time.getTimeInMillis();Log.v("Scheduling state change " + newState + " to instance " + instance.mId +" at " + AlarmUtils.getFormattedTime(context, time) + " (" + timeInMillis + ")");Intent stateChangeIntent = createStateChangeIntent(context, ALARM_MANAGER_TAG, instance,newState);PendingIntent pendingIntent = PendingIntent.getBroadcast(context, instance.hashCode(),stateChangeIntent, PendingIntent.FLAG_UPDATE_CURRENT);AlarmManager am = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);if (Utils.isKitKatOrLater()) {am.setExact(AlarmManager.RTC_WAKEUP, timeInMillis, pendingIntent);} else {am.set(AlarmManager.RTC_WAKEUP, timeInMillis, pendingIntent);}}
可以看出AlarmStateManager是系统闹钟APP用来管理闹钟状态的类。
我们可以看到AlarmManager的setExact和set函数。
if (Utils.isKitKatOrLater()) {am.setExact(AlarmManager.RTC_WAKEUP, timeInMillis, pendingIntent);} else {am.set(AlarmManager.RTC_WAKEUP, timeInMillis, pendingIntent);}
这里是RTC_WAKEUP, 这就保证了即使系统睡眠了,都能唤醒,闹钟工作。
综上,可以看出系统闹钟APPAlarmManager和开发应用层APP使用的是同样的AlarmManager。
接下来具体分析AlarmManager的实现即可。
框架层
Java层
在/frameworks/base/core/java/android/app/AlarmManager.java中:
public class AlarmManager
{private static final String TAG = "AlarmManager";/*** Alarm time in {@link System#currentTimeMillis System.currentTimeMillis()}* (wall clock time in UTC), which will wake up the device when* it goes off.*/public static final int RTC_WAKEUP = 0;/*** Alarm time in {@link System#currentTimeMillis System.currentTimeMillis()}* (wall clock time in UTC). This alarm does not wake the* device up; if it goes off while the device is asleep, it will not be* delivered until the next time the device wakes up.*/public static final int RTC = 1;/*** Alarm time in {@link android.os.SystemClock#elapsedRealtime* SystemClock.elapsedRealtime()} (time since boot, including sleep),* which will wake up the device when it goes off.*/public static final int ELAPSED_REALTIME_WAKEUP = 2;/*** Alarm time in {@link android.os.SystemClock#elapsedRealtime* SystemClock.elapsedRealtime()} (time since boot, including sleep).* This alarm does not wake the device up; if it goes off while the device* is asleep, it will not be delivered until the next time the device* wakes up.*/public static final int ELAPSED_REALTIME = 3;.../*** TBW: new 'exact' alarm that must be delivered as nearly as possible* to the precise time specified.*/public void setExact(int type, long triggerAtMillis, PendingIntent operation) {setImpl(type, triggerAtMillis, WINDOW_EXACT, 0, operation, null);}/** @hide */public void set(int type, long triggerAtMillis, long windowMillis, long intervalMillis,PendingIntent operation, WorkSource workSource) {setImpl(type, triggerAtMillis, windowMillis, intervalMillis, operation, workSource);}private void setImpl(int type, long triggerAtMillis, long windowMillis, long intervalMillis,PendingIntent operation, WorkSource workSource) {if (triggerAtMillis < 0) {/* NOTYETif (mAlwaysExact) {// Fatal error for KLP+ apps to use negative trigger timesthrow new IllegalArgumentException("Invalid alarm trigger time "+ triggerAtMillis);}*/triggerAtMillis = 0;}try {mService.set(type, triggerAtMillis, windowMillis, intervalMillis, operation,workSource);} catch (RemoteException ex) {}}...
}
主要分为4中模式:
RTC_WAKEUP:UTC时间,会在设备关闭时唤醒它
RTC:UTC时间,此警报不会唤醒设备;如果它在设备处于休眠状态时关闭,则在下次设备唤醒之前不会发送。
ELAPSED_REALTIME_WAKEUP:开机后的时间,包括睡眠,会在设备关闭时唤醒它
ELAPSED_REALTIME:开机后的时间,包括睡眠;如果它在设备处于休眠状态时关闭,则在下次设备唤醒之前不会发送。
setExact和set均调用了setImpl函数,setImpl函数中核心是:
mService.set(type, triggerAtMillis, windowMillis, intervalMillis, operation,workSource);
查看mService的定义:
private final IAlarmManager mService;
在/frameworks/base/core/java/android/app/IAlarmManager.aidl:
/*** System private API for talking with the alarm manager service.** {@hide}*/
interface IAlarmManager {/** windowLength == 0 means exact; windowLength < 0 means the let the OS decide */void set(int type, long triggerAtTime, long windowLength,long interval, in PendingIntent operation, in WorkSource workSource);void setTime(long millis);void setTimeZone(String zone);void remove(in PendingIntent operation);
}
可以看到是一个aidl文件,要用到进程间通信技术。
查找具体实现,在/frameworks/base/services/java/com/android/server/AlarmManagerService.java:
class AlarmManagerService extends IAlarmManager.Stub {...@Overridepublic void set(int type, long triggerAtTime, long windowLength, long interval,PendingIntent operation, WorkSource workSource) {if (workSource != null) {mContext.enforceCallingPermission(android.Manifest.permission.UPDATE_DEVICE_STATS,"AlarmManager.set");}set(type, triggerAtTime, windowLength, interval, operation, false, workSource);}public void set(int type, long triggerAtTime, long windowLength, long interval,PendingIntent operation, boolean isStandalone, WorkSource workSource) {... final long nowElapsed = SystemClock.elapsedRealtime();final long triggerElapsed = convertToElapsed(triggerAtTime, type);final long maxElapsed;if (windowLength == AlarmManager.WINDOW_EXACT) {maxElapsed = triggerElapsed;} else if (windowLength < 0) {maxElapsed = maxTriggerTime(nowElapsed, triggerElapsed, interval);} else {maxElapsed = triggerElapsed + windowLength;}synchronized (mLock) {if (DEBUG_BATCH) {Slog.v(TAG, "set(" + operation + ") : type=" + type+ " triggerAtTime=" + triggerAtTime + " win=" + windowLength+ " tElapsed=" + triggerElapsed + " maxElapsed=" + maxElapsed+ " interval=" + interval + " standalone=" + isStandalone);}setImplLocked(type, triggerAtTime, triggerElapsed, maxElapsed,interval, operation, isStandalone, true, workSource);}}private void setImplLocked(int type, long when, long whenElapsed, long maxWhen, long interval,PendingIntent operation, boolean isStandalone, boolean doValidate,WorkSource workSource) {...if (reschedule) {rescheduleKernelAlarmsLocked();} ...}private void rescheduleKernelAlarmsLocked() {// Schedule the next upcoming wakeup alarm. If there is a deliverable batch// prior to that which contains no wakeups, we schedule that as well.if (mAlarmBatches.size() > 0) {final Batch firstWakeup = findFirstWakeupBatchLocked();final Batch firstBatch = mAlarmBatches.get(0);if (firstWakeup != null && mNextWakeup != firstWakeup.start) {mNextWakeup = firstWakeup.start;setLocked(ELAPSED_REALTIME_WAKEUP, firstWakeup.start);}if (firstBatch != firstWakeup && mNextNonWakeup != firstBatch.start) {mNextNonWakeup = firstBatch.start;setLocked(ELAPSED_REALTIME, firstBatch.start);}}} private void setLocked(int type, long when){if (mDescriptor != -1){// The kernel never triggers alarms with negative wakeup times// so we ensure they are positive.long alarmSeconds, alarmNanoseconds;if (when < 0) {alarmSeconds = 0;alarmNanoseconds = 0;} else {alarmSeconds = when / 1000;alarmNanoseconds = (when % 1000) * 1000 * 1000;}set(mDescriptor, type, alarmSeconds, alarmNanoseconds);}else{Message msg = Message.obtain();msg.what = ALARM_EVENT;mHandler.removeMessages(ALARM_EVENT);mHandler.sendMessageAtTime(msg, when);}}...private native int init();private native void close(int fd);private native void set(int fd, int type, long seconds, long nanoseconds);private native int waitForAlarm(int fd);private native int setKernelTimezone(int fd, int minuteswest);...}
通过上述代码,AlarmManagerService的set函数最终调用了native的set函数进行设置。
同时,注意到AlarmManagerService中含有一个AlarmThread内部类:
private class AlarmThread extends Thread{public AlarmThread(){super("AlarmManager");}public void run(){ArrayList<Alarm> triggerList = new ArrayList<Alarm>();while (true){int result = waitForAlarm(mDescriptor);triggerList.clear();if ((result & TIME_CHANGED_MASK) != 0) {if (DEBUG_BATCH) {Slog.v(TAG, "Time changed notification from kernel; rebatching");}remove(mTimeTickSender);rebatchAllAlarms();mClockReceiver.scheduleTimeTickEvent();Intent intent = new Intent(Intent.ACTION_TIME_CHANGED);intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING| Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);mContext.sendBroadcastAsUser(intent, UserHandle.ALL);}synchronized (mLock) {final long nowRTC = System.currentTimeMillis();final long nowELAPSED = SystemClock.elapsedRealtime();if (localLOGV) Slog.v(TAG, "Checking for alarms... rtc=" + nowRTC+ ", elapsed=" + nowELAPSED);if (WAKEUP_STATS) {if ((result & IS_WAKEUP_MASK) != 0) {long newEarliest = nowRTC - RECENT_WAKEUP_PERIOD;int n = 0;for (WakeupEvent event : mRecentWakeups) {if (event.when > newEarliest) break;n++; // number of now-stale entries at the list head}for (int i = 0; i < n; i++) {mRecentWakeups.remove();}recordWakeupAlarms(mAlarmBatches, nowELAPSED, nowRTC);}}triggerAlarmsLocked(triggerList, nowELAPSED, nowRTC);rescheduleKernelAlarmsLocked();// now deliver the alarm intentsfor (int i=0; i<triggerList.size(); i++) {Alarm alarm = triggerList.get(i);try {if (localLOGV) Slog.v(TAG, "sending alarm " + alarm);alarm.operation.send(mContext, 0,mBackgroundIntent.putExtra(Intent.EXTRA_ALARM_COUNT, alarm.count),mResultReceiver, mHandler);...} catch (PendingIntent.CanceledException e) {if (alarm.repeatInterval > 0) {// This IntentSender is no longer valid, but this// is a repeating alarm, so toss the hoser.remove(alarm.operation);}} catch (RuntimeException e) {Slog.w(TAG, "Failure sending alarm.", e);}}}}}}
AlarmManagerService 中 AlarmThread 会一直调用waitForAlarm轮询,会返回一个值 。如果打开失败就直接返回,成功就会做一些动作,通过执行triggerAlarmsLocked,把几种类型的闹钟列表中符合要求的 alarm 添加到 triggerList 中,然后用 alarm.operation.send 发送消息,调起小闹钟程序
Native层
/frameworks/base/services/jni/com_android_server_AlarmManagerService.cpp
namespace android {static jint android_server_AlarmManagerService_setKernelTimezone(JNIEnv* env, jobject obj, jint fd, jint minswest)
{struct timezone tz;tz.tz_minuteswest = minswest;tz.tz_dsttime = 0;int result = settimeofday(NULL, &tz);if (result < 0) {ALOGE("Unable to set kernel timezone to %d: %s\n", minswest, strerror(errno));return -1;} else {ALOGD("Kernel timezone updated to %d minutes west of GMT\n", minswest);}return 0;
}static jint android_server_AlarmManagerService_init(JNIEnv* env, jobject obj)
{return open("/dev/alarm", O_RDWR);
}static void android_server_AlarmManagerService_close(JNIEnv* env, jobject obj, jint fd)
{close(fd);
}static void android_server_AlarmManagerService_set(JNIEnv* env, jobject obj, jint fd, jint type, jlong seconds, jlong nanoseconds)
{struct timespec ts;ts.tv_sec = seconds;ts.tv_nsec = nanoseconds;int result = ioctl(fd, ANDROID_ALARM_SET(type), &ts);if (result < 0){ALOGE("Unable to set alarm to %lld.%09lld: %s\n", seconds, nanoseconds, strerror(errno));}
}static jint android_server_AlarmManagerService_waitForAlarm(JNIEnv* env, jobject obj, jint fd)
{int result = 0;do{result = ioctl(fd, ANDROID_ALARM_WAIT);} while (result < 0 && errno == EINTR);if (result < 0){ALOGE("Unable to wait on alarm: %s\n", strerror(errno));return 0;}return result;
}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},
};int register_android_server_AlarmManagerService(JNIEnv* env)
{return jniRegisterNativeMethods(env, "com/android/server/AlarmManagerService",sMethods, NELEM(sMethods));
}} /* namespace android */
android_server_AlarmManagerService_set中关键语句:
int result = ioctl(fd, ANDROID_ALARM_SET(type), &ts);
在计算机中,ioctl(input/output control)是一个专用于设备输入输出操作的系统调用,该调用传入一个跟设备有关的请求码,系统调用的功能完全取决于请求码。
参考ioctl介绍博文
接下来我们需要查看内核层的处理方式,ANDROID_ALARM_SET为关键字查找。
内核层
在/drivers/rtc/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;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;...default:rv = -EINVAL;goto err1;}
err1:return rv;
}
调用了alarm_start_range 设置闹钟, alarm_set_rtc设置RTC
这两个函数在 android_alarm.h 声明,在 alarm.c 里实现
/include/linux/android_alarm.h:
#ifndef _LINUX_ANDROID_ALARM_H
#define _LINUX_ANDROID_ALARM_H#include <linux/ioctl.h>
#include <linux/time.h>enum android_alarm_type {/* return code bit numbers or set alarm arg */ANDROID_ALARM_RTC_WAKEUP,ANDROID_ALARM_RTC,ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP,ANDROID_ALARM_ELAPSED_REALTIME,ANDROID_ALARM_SYSTEMTIME,ANDROID_ALARM_TYPE_COUNT,/* return code bit numbers *//* ANDROID_ALARM_TIME_CHANGE = 16 */
};#ifdef __KERNEL__#include <linux/ktime.h>
#include <linux/rbtree.h>/** The alarm interface is similar to the hrtimer interface but adds support* for wakeup from suspend. It also adds an elapsed realtime clock that can* be used for periodic timers that need to keep runing while the system is* suspended and not be disrupted when the wall time is set.*//*** struct alarm - the basic alarm structure* @node: red black tree node for time ordered insertion* @type: alarm type. rtc/elapsed-realtime/systemtime, wakeup/non-wakeup.* @softexpires: the absolute earliest expiry time of the alarm.* @expires: the absolute expiry time.* @function: alarm expiry callback function** The alarm structure must be initialized by alarm_init()**/struct alarm {struct rb_node node;enum android_alarm_type type;ktime_t softexpires;ktime_t expires;void (*function)(struct alarm *);
};void alarm_init(struct alarm *alarm,enum android_alarm_type type, void (*function)(struct alarm *));
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);#endifenum android_alarm_return_flags {ANDROID_ALARM_RTC_WAKEUP_MASK = 1U << ANDROID_ALARM_RTC_WAKEUP,ANDROID_ALARM_RTC_MASK = 1U << ANDROID_ALARM_RTC,ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP_MASK =1U << ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP,ANDROID_ALARM_ELAPSED_REALTIME_MASK =1U << ANDROID_ALARM_ELAPSED_REALTIME,ANDROID_ALARM_SYSTEMTIME_MASK = 1U << ANDROID_ALARM_SYSTEMTIME,ANDROID_ALARM_TIME_CHANGE_MASK = 1U << 16
};/* Disable alarm */
#define ANDROID_ALARM_CLEAR(type) _IO('a', 0 | ((type) << 4))/* Ack last alarm and wait for next */
#define ANDROID_ALARM_WAIT _IO('a', 1)#define ALARM_IOW(c, type, size) _IOW('a', (c) | ((type) << 4), size)
/* Set alarm */
#define ANDROID_ALARM_SET(type) ALARM_IOW(2, type, struct timespec)
#define ANDROID_ALARM_SET_AND_WAIT(type) ALARM_IOW(3, type, struct timespec)
#define ANDROID_ALARM_GET_TIME(type) ALARM_IOW(4, type, struct timespec)
#define ANDROID_ALARM_SET_RTC _IOW('a', 5, struct timespec)
#define ANDROID_ALARM_BASE_CMD(cmd) (cmd & ~(_IOC(0, 0, 0xf0, 0)))
#define ANDROID_ALARM_IOCTL_TO_TYPE(cmd) (_IOC_NR(cmd) >> 4)#endif
/drivers/rtc/alarm.c:
/*** alarm_start_range - (re)start an alarm* @alarm: the alarm to be added* @start: earliest expiry time* @end: expiry time*/
void alarm_start_range(struct alarm *alarm, ktime_t start, ktime_t end)
{unsigned long flags;spin_lock_irqsave(&alarm_slock, flags);alarm->softexpires = start;alarm->expires = end;alarm_enqueue_locked(alarm);spin_unlock_irqrestore(&alarm_slock, flags);
}.../*** alarm_set_rtc - set the kernel and rtc walltime* @new_time: timespec value containing the new time*/
int alarm_set_rtc(struct timespec new_time)
{...ret = rtc_set_time(alarm_rtc_dev, &rtc_new_rtc_time);...return ret;
}static int alarm_suspend(struct platform_device *pdev, pm_message_t state)static int alarm_resume(struct platform_device *pdev)
/drivers/rtc/interface.c:
int rtc_set_time(struct rtc_device *rtc, struct rtc_time *tm)
{int err;err = rtc_valid_tm(tm);if (err != 0)return err;err = mutex_lock_interruptible(&rtc->ops_lock);if (err)return err;if (!rtc->ops)err = -ENODEV;else if (rtc->ops->set_time)err = rtc->ops->set_time(rtc->dev.parent, tm);else if (rtc->ops->set_mmss) {unsigned long secs;err = rtc_tm_to_time(tm, &secs);if (err == 0)err = rtc->ops->set_mmss(rtc->dev.parent, secs);} elseerr = -EINVAL;mutex_unlock(&rtc->ops_lock);return err;
}
EXPORT_SYMBOL_GPL(rtc_set_time);
在 rtc->ops->set_time(rtc->dev.parent, tm)中set_time 就看到具体的是那个RTC芯片,这里不再继续往下分析。
alarm.c 文件实现的是所有 alarm 设备的通用性操作,它创建了一个设备 class
alarm_dev.c 则创建具体的 alarm 设备,注册到该设备 class 中。
alarm.c 还实现了与 interface.c 的接口,即建立了与具体 rtc 驱动和 rtc 芯片的联系。
alarm_dev.c 在 alarm.c 基础包装了一层,主要是实现了标准的 miscdevice 接口,提供给应用层调用。
可以这样概括:alarm.c 实现的是机制和框架,alarm_dev.c 则是实现符合这个框架的设备驱动,alarm_dev.c 相当于在底层硬件 rtc 闹钟功能的基础上虚拟了多个软件闹钟。
alarm.c 里面实现了 alarm_suspend alarm_resume 函数。如果不需要唤醒系统,设置闹钟并不会往rtc 芯片的寄存器上写数据,通过上层写到设备文件/dev/alarm 里面,AlarmThread 会不停的去轮寻下一个时间有没有闹钟,直接从设备文件 /dev/alarm 里面读取。
如果需要唤醒系统,alarm 的alarm_suspend就会写到下层的rtc芯片的寄存器上去, 然后即使系统suspend之后,闹钟通过rtc 也能唤醒系统。
综上:Alarm 闹钟是 android 系统中在标准 RTC 驱动上开发的一个新的驱动,提供了一个定时器,用于把设备从睡眠状态唤醒,当然因为它是依赖 RTC 驱动的,所以它同时还可以为系统提供一个掉电下还能运行的实时时钟。
当系统断电时,主板上的 rtc 芯片将继续维持系统的时间,这样保证再次开机后系统的时间不会错误。当系统开始时,内核从 RTC 中读取时间来初始化系统时间,关机时便又将系统时间写回到 rtc 中,关机阶段将有主板上另外的电池来供应 rtc 计时。Android 中的 Alarm在设备处于睡眠模式时仍保持活跃,它可以设置来唤醒设备。
欢迎关注我的公众号,持续分析优质技术文章
Android Alarm机制分析相关推荐
- Android 系统(177)---Android消息机制分析:Handler、Looper、MessageQueue源码分析
Android消息机制分析:Handler.Looper.MessageQueue源码分析 1.前言 关于Handler消息机制的博客实际上是非常多的了. 之前也是看别人的博客过来的,但是过了一段时间 ...
- android binder机制分析 以MediaPlayer为例子
(一) 概述 android的binder机制提供一种进程间通信的方法,使不同一个进程可以以类 似远程过程调用的形式调用另一个进程所提供的功能.binder机制在Java环境和 C/C++环境都有提供 ...
- Android WindowManagerService机制分析:窗口的显示层级
WindowManagerService(以下简称WMS)是Android Framework中一个重要的系统服务,用来管理系统中窗口(Window)的行为.Window是一个抽象的概念,它是一个矩形 ...
- android art源码分析,Android ART机制分析
本文章由Jack_Jia编写,转载请注明出处.文章链接:外链网址已屏蔽 作者:Jack_Jia邮箱: 2013年度"博客之星"投票火热进行中,欢迎投票支持我: 一.Android系 ...
- Android Handler机制分析
转自:http://blog.csdn.net/stonecao/article/details/6417364 在android中提供了一种异步回调机制Handler,使用它,我们可以在完成一个很长 ...
- android系统的alarm机制,Android中Alarm的机制
本次给大家分析的是Android中Alarm的机制所用源码为最新的Android4.4.4.首先简单介绍如何使用Alarm并给出其工作原理,接着分析Alarm和Timer以及Handler在完成定时任 ...
- Android平台安全分析
摘 要 Android的流行使其已成为众多恶意软件的***目标,针对Android的***.恶意软件和应用层特权提升***等安全威胁不断出现.同时,Android源代码开放的特性也吸引了研究人员的广泛 ...
- Android系统中的Binder通信机制分析(7)- Java 层的 Binder 机制
声明 其实对于Android系统Binder通信的机制早就有分析的想法,记得2019年6.7月份Mr.Deng离职期间约定一起对其进行研究的,但因为我个人问题没能实施这个计划,留下些许遗憾- 文中参考 ...
- Android Broadcast广播机制分析
基于Android 6.0的源码剖析, 分析android广播的发送与接收流程. 一.概述 广播(Broadcast)机制用于进程/线程间通信,广播分为广播发送和广播接收两个过程,其中广播接收者Bro ...
- Android架构分析之Android消息处理机制(一)
作者:刘昊昱 博客:http://blog.csdn.net/liuhaoyutz Android版本号:4.4.2 在这个系列文章中我们将来分析Android消息处理机制. 本文介绍了一个使用Han ...
最新文章
- “大龄”码农的“中年危机”:35岁之后,该如何应对?
- arm linux gcc fpic,【待整理】Gcc中编译和链接选项 -fpic -fPIC -fpie -fPIE -pie的含义
- XHTML 相对路径与绝对路径
- 感受JTable 与 JTableModel
- 教你实现图片的惰性加载
- vue = 什么意思_Vue导入模块import xxx from '@/xxx'中的@是什么含义?
- windows查看usb信息命令_Linux dumpe2fs命令:查看文件系统信息
- 早该知道的 7 个JavaScript 技巧[转]
- 在CNN网络中roi从原图映射到feature map中的计算方法
- 支付宝没有优势了,五大银行宣布今起手机银行转账汇款免收手续费
- springboot项目搭建0000-导航篇
- Oracle Data purge(Oracle一键数据删除)
- 纯干货!live2d动画制作简述以及踩坑
- windows对话框窗口DialogBox模式对话框、EndDialog、CreateDialog非模式对话框、DestroyWindow、WM_INITDIALOG
- 如何下载MySQL各个版本
- outlook 2007 通讯录分组导出导入
- 2022年中国研究生报考现状分析:考研热度只增不减,就业压力仍为主要因素[图]
- 专访迅游科技袁旭:聚焦三大核心业务板块,差异化网络通信能力让赛道不断拓宽...
- 模式识别Pattern Classification (DHS)英文版原著+中文版翻译+课后答案分享
- 3DSMax常用快捷键
热门文章
- Keep It Simple and Stupid是什么意思
- 军团要塞2无限子弹服务器,【图片】教大家创建tf2服务器(互联网的)小白版~【军团要塞吧】_百度贴吧...
- UFS Write Booster Feature Overview
- linux系统清理命令行,告诉你Ubuntu系统较全面清理的方法及命令
- Visual SourceSafe基本操作
- 如何完全禁止win10自动更新(自动升级)
- Wcf 文件上传下载
- 茶酒“银行”的梦醒时分
- 基于深度学习机器学习的中文期刊分类
- 有什么好玩的网页小游戏网站推荐么?