版权说明:本文为 开开向前冲 原创文章,转载请注明出处;
注:限于作者水平有限,文中有不对的地方还请指教
项目需求:AP需要在开机24小时后自检重启;
针对上述需求,我们首先想想有哪些实现方法呢?
- 1:开机接收广播,启动一个常驻服务,在服务中轮训。轮训的方式可以通过Handler或者启动一个常驻服务,在服务中开启线程中做死循环;
- 2:使用Timer 来定时操作;
- 3:使用AlarmManager 来实现定时操作功能;
上述三个方法看似都可行;实际上只有最后一种可行,前两种都是不可行的,为什么不可行呢?
一:服务轮训:Handler发送消息,消息的发送依赖于Handler 线程,如果线程结束,GG!那开启一个常驻服务呢,在服务中开启一个线程做死循环,我们先不说省电问题,如果系统进入深度睡眠,即使这个while(1)的循环也不能得到执行;
二:Timer:Timer的问题也是在于如果系统进入深度睡眠,将无法唤醒;
所以只有AlarmManager。可以通过AlarmManager定时唤醒系统执行任务,即使系统处于深度睡眠也能唤醒——这就和我们的闹钟差不多;也能省电;那使用AlarmManager如果做呢?
代码敬上:
AlarmManager aManager = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
Intent in = new Intent();
in.setClass(context, RebootService.class);
PendingIntent pi = PendingIntent.getService(context, 0, in, 0);
aManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,24 * 60 * 60 * 1000, pi);
没错,AlarmManager的使用就是这个简单,在开机后24小时系统将会启动RebootService,RebootService是我自己实现的一个服务,在这个服务中任意Lifecycle中实现关机操作就OK;
PowerManager pm = (PowerManager)this.getSystemService(Context.POWER_SERVICE);
pm.reboot("self-inspection");
AlarmManager不仅可以唤醒服务,也可以发送广播,也可以调起Activity;需要将上述PendingIntent.getService()修改成对应的PendingIntent.getActivity()或者PendingIntent.getBroadcast()方法;
因为之前有维护过原生Clock APP,对AlarmManager有了解,正好借此机会完整机会阐述一下AlarmManager;
AlarmManager 对象获取:
AlarmManager aManager = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
AlarmManager 常用接口:
- set(int type, long triggerAtMillis, PendingIntent operation)
一次性任务; - setRepeating(int type, long triggerAtMillis, long intervalMillis, PendingIntent operation)
重复任务;时间固定; - setInexactRepeating(int type, long triggerAtMillis, long intervalMillis, PendingIntent operation)
重复任务,时间不固定; - cancel(PendingIntent operation)
取消上述设置的定时任务;此时PendingIntent务必和需要取消的任务的PendingIntent一模一样;
这里主要介绍了几个常用于定时任务的接口;关于AlarmManager 的接口和用法,
可以参考Google官方API文档AlarmManager 文档;
接口参数详解:
- int type
AlarmManager.RTC:硬件时间,不唤醒休眠设备;当休眠时不发起闹钟。
AlarmManager.RTC_WAKEUP:硬件时间,当闹钟发射时唤醒休眠设备;
AlarmManager.ELAPSED_REALTIME:真实时间流逝,不唤醒休眠设备;当设备休眠时不发起闹钟。
AlarmManager.ELAPSED_REALTIME_WAKEUP:真实时间流逝,当闹钟发起时唤醒手机休眠;
RTC闹钟和ELAPSED_REALTIME 最大的差别就是前者可以通过修改手机时间触发闹钟事件,
后者要通过真实时间的流逝,即使在休眠状态,时间也会被计算。
- long triggerAtMillis : 闹钟第一次执行时间,毫秒为单位,需与第一个type参数匹配,
- 如果是RTC类型,triggerAtMillis 则一般使用System.currentTimeMillis();
- 如果是ELAPSED类型,triggerAtMillis 则一般使用SystemClock.elapsedRealtime();
- long intervalMillis : 两次闹钟执行间隔
- PendingIntent operation : 任务的执行动作,发送广播,启动activity,启动service
上述大概讲了AlarmManager接口如何使用,作为一名System Engineer,我们还需要研究研究真实的服务提供者AlarmManagerService:
从上述名字中我们知道AlarmManager 只是AlarmManagerService的代理,实际实现都是在AlarmManagerService中实现的;
AlarmManagerService启动
frameworks\base\services\java\com\android\server\SystemServer.java
private void startOtherServices() {
...
mAlarmManagerService = mSystemServiceManager.startService(AlarmManagerService.class);
//mSystemServiceManager 即是SystemServiceManger
alarm = IAlarmManager.Stub.asInterface(
ServiceManager.getService(Context.ALARM_SERVICE));
...
}
frameworks\base\services\core\java\com\android\server\SystemServiceManager.java
public T startService(Class serviceClass) {
...
Constructor constructor = serviceClass.getConstructor(Context.class);
service = constructor.newInstance(mContext);
...
mServices.add(service);
service.onStart(); //service 即是AlarmManagerService
}
frameworks\base\services\core\java\com\android\server\AlarmManagerService.java
@Override
public void onStart() {
mNativeData = init();//init方法是native 方法,调用JNI
mNextWakeup = mNextNonWakeup = 0;
// We have to set current TimeZone info to kernel
// because kernel doesn't keep this after reboot
setTimeZoneImpl(SystemProperties.get(TIMEZONE_PROPERTY));
PowerManager pm = (PowerManager) getContext().getSystemService(Context.POWER_SERVICE);
mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "*alarm*");
mTimeTickSender = PendingIntent.getBroadcastAsUser(getContext(), 0,
new Intent(Intent.ACTION_TIME_TICK).addFlags(
Intent.FLAG_RECEIVER_REGISTERED_ONLY
| Intent.FLAG_RECEIVER_FOREGROUND), 0,
UserHandle.ALL);
Intent intent = new Intent(Intent.ACTION_DATE_CHANGED);
intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
mDateChangeSender = PendingIntent.getBroadcastAsUser(getContext(), 0, intent,
Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT, UserHandle.ALL);
// now that we have initied the driver schedule the alarm
mClockReceiver = new ClockReceiver();
mClockReceiver.scheduleTimeTickEvent();
mClockReceiver.scheduleDateChangedEvent();
mInteractiveStateReceiver = new InteractiveStateReceiver();
mUninstallReceiver = new UninstallReceiver();
/*
上述代码注册了各种监听器,监听各种和时间变化相关的事务
*/
if (mNativeData != 0) {
AlarmThread waitThread = new AlarmThread(); //创建AlarmThread
waitThread.start();
} else {
Slog.w(TAG, "Failed to open alarm driver. Falling back to a handler.");
}
publishBinderService(Context.ALARM_SERVICE, mService); //注册服务,调用ServiceManager.addService添加服务;
}
最后的publishBinderService即调用ServiceManager.addService注册服务,服务
AlarmManagerService的onStart方法工作
1. 由于重启内核没有时区信息,需要将时区信息保存到内核;
2. 创建ClockReceiver用于监听TIME_TICK和DATE_CHANGED广播;
3. 创建InteractiveStateReceiver,用于监听亮屏/灭屏广播;
4. 创建UninstallReceiver,用于监听package移除/重启,sdcard不可用的广播;
5. 创建线程”AlarmManager”;
6. 注册服务。
AlarmManagerService中的native JNI方法:
private native long init();
private native void close(long nativeData);
private native void set(long nativeData, int type, long seconds, long nanoseconds);
private native void clear(long nativeData, int type, long seconds, long nanoseconds);
private native int waitForAlarm(long nativeData);
private native int setKernelTime(long nativeData, long millis);
private native int setKernelTimezone(long nativeData, int minuteswest);
我们根据Android JNI 文件命名规范(报名中"."替换为"_")知道对应的JNI文件名为com_android_server_AlarmManagerService.cpp
static JNINativeMethod sMethods[] = {
/* name, signature, funcPtr */
{"init", "()J", (void*)android_server_AlarmManagerService_init},
{"close", "(J)V", (void*)android_server_AlarmManagerService_close},
{"set", "(JIJJ)V", (void*)android_server_AlarmManagerService_set},
{"clear", "(JIJJ)V", (void*)android_server_AlarmManagerService_clear},
{"waitForAlarm", "(J)I", (void*)android_server_AlarmManagerService_waitForAlarm},
{"setKernelTime", "(JJ)I", (void*)android_server_AlarmManagerService_setKernelTime},
{"setKernelTimezone", "(JI)I", (void*)android_server_AlarmManagerService_setKernelTimezone},
};
上述说道在AlarmManagerService.java中onStart方法中有调用native JNI init()方法;
static jlong android_server_AlarmManagerService_init(JNIEnv*, jobject)
{
jlong ret = init_alarm_driver(); //初始化alarm driver
if (ret) {
return ret;
}
return init_timerfd(); //初始化文件描述符
}
static jlong init_alarm_driver()
{
int fd = open("/dev/alarm", O_RDWR); //打开节点/dev/alarm,并创建Alarm驱动对象。
if (fd < 0) {
ALOGV("opening alarm driver failed: %s", strerror(errno));
return 0;
}
AlarmImpl *ret = new AlarmImplAlarmDriver(fd);//创建AlarmImplAlarmDriver对象
return reinterpret_cast(ret);
}
static jlong init_timerfd()
{
int epollfd;
int fds[N_ANDROID_TIMERFDS];
......
epollfd = epoll_create(N_ANDROID_TIMERFDS);
for (size_t i = 0; i < N_ANDROID_TIMERFDS; i++) {
fds[i] = timerfd_create(android_alarm_to_clockid[i], 0);
......
}
AlarmImpl *ret = new AlarmImplTimerFd(fds, epollfd);//创建AlarmImplTimerFd对象
for (size_t i = 0; i < N_ANDROID_TIMERFDS; i++) {
epoll_event event;
event.events = EPOLLIN | EPOLLWAKEUP;
event.data.u32 = i;
int err = epoll_ctl(epollfd, EPOLL_CTL_ADD, fds[i], &event);
if (err < 0) {
ALOGV("epoll_ctl(EPOLL_CTL_ADD) failed: %s", strerror(errno));
delete ret;
return 0;
}
}
struct itimerspec spec;
memset(&spec, 0, sizeof(spec));
/* 0 = disarmed; the timerfd doesn't need to be armed to get
RTC change notifications, just set up as cancelable */
int err = timerfd_settime(fds[ANDROID_ALARM_TYPE_COUNT],
TFD_TIMER_ABSTIME | TFD_TIMER_CANCEL_ON_SET, &spec, NULL);
if (err < 0) {
ALOGV("timerfd_settime() failed: %s", strerror(errno));
delete ret;
return 0;
}
return reinterpret_cast(ret);
}
//android_alarm_to_clockid 数组的定义
android_alarm_to_clockid[N_ANDROID_TIMERFDS] = {
CLOCK_REALTIME_ALARM,
CLOCK_REALTIME,
CLOCK_BOOTTIME_ALARM,
CLOCK_BOOTTIME,
CLOCK_MONOTONIC,
CLOCK_REALTIME,
};
AlarmThread
private class AlarmThread extends Thread
{
public AlarmThread()
{
super("AlarmManager");
}
public void run()
{
ArrayList triggerList = new ArrayList();
while (true) //无线循环
{
int result = waitForAlarm(mNativeData); //JNI 方法,使用EPOLL监听FD,等待驱动返回执行下面的分发执行;
triggerList.clear();
if ((result & TIME_CHANGED_MASK) != 0) {
if (DEBUG_BATCH) {
Slog.v(TAG, "Time changed notification from kernel; rebatching");
}
removeImpl(mTimeTickSender);
rebatchAllAlarms();
mClockReceiver.scheduleTimeTickEvent();
synchronized (mLock) {
mNumTimeChanged++;
}
Intent intent = new Intent(Intent.ACTION_TIME_CHANGED);
intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING
| Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
| Intent.FLAG_RECEIVER_FOREGROUND);
getContext().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);
}
}
boolean hasWakeup = triggerAlarmsLocked(triggerList, nowELAPSED, nowRTC);
if (SystemProperties.getInt("sys.quickboot.enable", 0) == 1) {
filtQuickBootAlarms(triggerList);
}
if (!hasWakeup && checkAllowNonWakeupDelayLocked(nowELAPSED)) {
// if there are no wakeup alarms and the screen is off, we can
// delay what we have so far until the future.
if (mPendingNonWakeupAlarms.size() == 0) {
mStartCurrentDelayTime = nowELAPSED;
mNextNonWakeupDeliveryTime = nowELAPSED
+ ((currentNonWakeupFuzzLocked(nowELAPSED)*3)/2);
}
mPendingNonWakeupAlarms.addAll(triggerList);
mNumDelayedAlarms += triggerList.size();
rescheduleKernelAlarmsLocked();
updateNextAlarmClockLocked();
} else {
// now deliver the alarm intents; if there are pending non-wakeup
// alarms, we need to merge them in to the list. note we don't
// just deliver them first because we generally want non-wakeup
// alarms delivered after wakeup alarms.
rescheduleKernelAlarmsLocked();
updateNextAlarmClockLocked();
if (mPendingNonWakeupAlarms.size() > 0) {
calculateDeliveryPriorities(mPendingNonWakeupAlarms);
triggerList.addAll(mPendingNonWakeupAlarms);
Collections.sort(triggerList, mAlarmDispatchComparator);
final long thisDelayTime = nowELAPSED - mStartCurrentDelayTime;
mTotalDelayTime += thisDelayTime;
if (mMaxDelayTime < thisDelayTime) {
mMaxDelayTime = thisDelayTime;
}
mPendingNonWakeupAlarms.clear();
}
deliverAlarmsLocked(triggerList, nowELAPSED); //alarm 事件分发
}
}
}
}
}
waitForAlarm是一个native方法具体的实现在驱动中。如果整个系统中没有alarm的时间回调,
waitForAlarm则阻塞在这,直到有回调的时候才往后执行,这样会减少CPU的开销。
com_android_server_AlarmManagerService.cpp waitForAlarm()
int AlarmImplTimerFd::waitForAlarm()
{
epoll_event events[N_ANDROID_TIMERFDS];
int nevents = epoll_wait(epollfd, events, N_ANDROID_TIMERFDS, -1);//监听事件
if (nevents < 0) {
return nevents;
}
int result = 0;
for (int i = 0; i < nevents; i++) {
uint32_t alarm_idx = events[i].data.u32;
uint64_t unused;
ssize_t err = read(fds[alarm_idx], &unused, sizeof(unused));
if (err < 0) {
if (alarm_idx == ANDROID_ALARM_TYPE_COUNT && errno == ECANCELED) {
result |= ANDROID_ALARM_TIME_CHANGE_MASK;
} else {
return err;
}
} else {
result |= (1 << alarm_idx);
}
}
return result;
}
deliverAlarmsLocked() —— AlarmThread事件中分发Alarm事件
void deliverAlarmsLocked(ArrayList triggerList, long nowELAPSED) {
mLastAlarmDeliveryTime = nowELAPSED;
for (int i=0; i 0) {
for (int wi=0; wi 0) {
// This IntentSender is no longer valid, but this
// is a repeating alarm, so toss the hoser.
removeImpl(alarm.operation);
}
} catch (RuntimeException e) {
Slog.w(TAG, "Failure sending alarm.", e);
}
}
}
PendingIntent.java
public void send(Context context, int code, @Nullable Intent intent,
@Nullable OnFinished onFinished, @Nullable Handler handler,
@Nullable String requiredPermission, @Nullable Bundle options)
throws CanceledException {
try {
String resolvedType = intent != null ?
intent.resolveTypeIfNeeded(context.getContentResolver())
: null;
int res = mTarget.send(code, intent, resolvedType, //////////mTarget 是PendingIntentRecord
onFinished != null
? new FinishedDispatcher(this, onFinished, handler)
: null,
requiredPermission, options);
...
} catch (RemoteException e) {
throw new CanceledException(e);
}
}
mTarget 是什么呢???
我们前面讲述AlarmManger使用时讲述会通过PendingIntent.getActivity或者getService吧;
PendingIntent 常用的几个静态方法:
PendingIntent.getActivity
PendingIntent.getService
PendingIntent.getBroadcast
PendingIntent.getBroadcastAsUser
mTarget就是在上述方法中创建PendingIntent时创建的。上述几个方法最终都会调用ActivityManagerService的getIntentSender方法
public static PendingIntent getService(Context context, int requestCode,
Intent intent, int flags) {
String packageName = context.getPackageName();
String resolvedType = intent != null ? intent.resolveTypeIfNeeded(
context.getContentResolver()) : null;
try {
intent.prepareToLeaveProcess();
IIntentSender target =
ActivityManagerNative.getDefault().getIntentSender( // ActivityManagerNative.getDefault()获取的是
//AMS的代理ActivityMangerProxy,binder call 调用AMS的相关方法
ActivityManager.INTENT_SENDER_SERVICE, packageName,
null, null, requestCode, new Intent[] { intent },
resolvedType != null ? new String[] { resolvedType } : null,
flags, null, UserHandle.myUserId());
return target != null ? new PendingIntent(target) : null;
} catch (RemoteException e) {
}
return null;
}
ActivityMangerService.java的 getIntentSender()获取的是PendingIntentRecord对象, 而该对象继承于IIntentSender.Stub, 经过binder call回来, 所以此处target是指PendingIntentRecord对象的代理端, 即为PendingIntent.mTarget,所以上述最终会调用PendingIntentRecord的send方法;具体的唤起Activity,Service等具体业务就是在这个send 方法中完成;
下面完整看一下AlarmManager 的set接口操作过程;
AlarmManager.java
public void set(int type, long triggerAtMillis, PendingIntent operation) {
setImpl(type, triggerAtMillis, legacyExactLength(), 0, operation, null, null);
}
private void setImpl(int type, long triggerAtMillis, long windowMillis, long intervalMillis,
PendingIntent operation, WorkSource workSource, AlarmClockInfo alarmClock) {
if (triggerAtMillis < 0) {
/* NOTYET
if (mAlwaysExact) {
// Fatal error for KLP+ apps to use negative trigger times
throw new IllegalArgumentException("Invalid alarm trigger time "
+ triggerAtMillis);
}
*/
triggerAtMillis = 0;
}
try {
mService.set(type, triggerAtMillis, windowMillis, intervalMillis, operation,
workSource, alarmClock); //这里的mService就是前面的AlarmManagerService中的mService对象。
} catch (RemoteException ex) {
}
}
//AlarmManager 类的构造函数
AlarmManager(IAlarmManager service, Context ctx) {
mService = service;
final int sdkVersion = ctx.getApplicationInfo().targetSdkVersion;
mAlwaysExact = (sdkVersion < Build.VERSION_CODES.KITKAT);
}
ContextImpl.java
AlarmManager对象的获取:(AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
@Override
public Object getSystemService(String name) {
ServiceFetcher fetcher = SYSTEM_SERVICE_MAP.get(name);
return fetcher == null ? null : fetcher.getService(this);
}
private static void registerService(String serviceName, ServiceFetcher fetcher) {
if (!(fetcher instanceof StaticServiceFetcher)) {
fetcher.mContextCacheIndex = sNextPerContextServiceCacheIndex++;
}
SYSTEM_SERVICE_MAP.put(serviceName, fetcher); //添加service到SYSTEM_SERVICE_MAP
}
registerService(ALARM_SERVICE, new ServiceFetcher() { //注册
public Object createService(ContextImpl ctx) {
IBinder b = ServiceManager.getService(ALARM_SERVICE);
IAlarmManager service = IAlarmManager.Stub.asInterface(b);
return new AlarmManager(service, ctx);
}});
/*package*/ static class ServiceFetcher {
int mContextCacheIndex = -1;
/**
* Main entrypoint; only override if you don't need caching.
*/
public Object getService(ContextImpl ctx) {
ArrayList
所以getSystemService中返回的就是AlarmManagerService 中mService 的代理;即AlarmManager中mService变量即是AlarmManagerService中mService的代理;AlarmManager中方法最终都是有AlarmManagerService的mService处理
关于上述AlarmManager中set方法的处理,调用
mService.set(type, triggerAtMillis, windowMillis, intervalMillis, operation,
workSource, alarmClock); //这里的mService就是前面的AlarmManagerService中的mService对象。
Binder Call 到AlarmMangerService中
本段代码是截取Android 5.1源码;
@Override
public void set(int type, long triggerAtTime, long windowLength, long interval,
PendingIntent operation, WorkSource workSource,
AlarmManager.AlarmClockInfo alarmClock) {
if (workSource != null) {
getContext().enforceCallingPermission(
android.Manifest.permission.UPDATE_DEVICE_STATS,
"AlarmManager.set");
}
setImpl(type, triggerAtTime, windowLength, interval, operation,
windowLength == AlarmManager.WINDOW_EXACT, workSource, alarmClock);
}
void setImpl(int type, long triggerAtTime, long windowLength, long interval,
PendingIntent operation, boolean isStandalone, WorkSource workSource,
AlarmManager.AlarmClockInfo alarmClock) {
if (operation == null) {
Slog.w(TAG, "set/setRepeating ignored because there is no intent");
return;
}
// Sanity check the window length. This will catch people mistakenly
// trying to pass an end-of-window timestamp rather than a duration.
if (windowLength > AlarmManager.INTERVAL_HALF_DAY) {
Slog.w(TAG, "Window length " + windowLength
+ "ms suspiciously long; limiting to 1 hour");
windowLength = AlarmManager.INTERVAL_HOUR;
}
// Sanity check the recurrence interval. This will catch people who supply
// seconds when the API expects milliseconds.
if (interval > 0 && interval < MIN_INTERVAL) {
Slog.w(TAG, "Suspiciously short interval " + interval
+ " millis; expanding to " + (int)(MIN_INTERVAL/1000)
+ " seconds");
interval = MIN_INTERVAL;
}
if (type < RTC_WAKEUP || type > RTC_POWEROFF_WAKEUP) {
throw new IllegalArgumentException("Invalid alarm type " + type);
}
if (triggerAtTime < 0) {
final long who = Binder.getCallingUid();
final long what = Binder.getCallingPid();
Slog.w(TAG, "Invalid alarm trigger time! " + triggerAtTime + " from uid=" + who
+ " pid=" + what);
triggerAtTime = 0;
}
final long nowElapsed = SystemClock.elapsedRealtime();
final long nominalTrigger = convertToElapsed(triggerAtTime, type);
// Try to prevent spamming by making sure we aren't firing alarms in the immediate future
final long minTrigger = nowElapsed + MIN_FUTURITY;
final long triggerElapsed = (nominalTrigger > minTrigger) ? nominalTrigger : minTrigger;
final long maxElapsed;
if (windowLength == AlarmManager.WINDOW_EXACT) {
maxElapsed = triggerElapsed;
} else if (windowLength < 0) {
maxElapsed = maxTriggerTime(nowElapsed, triggerElapsed, interval);
} else {
maxElapsed = triggerElapsed + windowLength;
}
final int userId = UserHandle.getCallingUserId();
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, windowLength, maxElapsed,
interval, operation, isStandalone, true, workSource, alarmClock, userId);
}
}
private void setImplLocked(int type, long when, long whenElapsed, long windowLength,
long maxWhen, long interval, PendingIntent operation, boolean isStandalone,
boolean doValidate, WorkSource workSource, AlarmManager.AlarmClockInfo alarmClock,
int userId) {
Alarm a = new Alarm(type, when, whenElapsed, windowLength, maxWhen, interval,
operation, workSource, alarmClock, userId);
removeLocked(operation);
int whichBatch = (isStandalone) ? -1 : attemptCoalesceLocked(whenElapsed, maxWhen);
if (whichBatch < 0) {
Batch batch = new Batch(a);
batch.standalone = isStandalone;
addBatchLocked(mAlarmBatches, batch);
} else {
Batch batch = mAlarmBatches.get(whichBatch);
if (batch.add(a)) {
// The start time of this batch advanced, so batch ordering may
// have just been broken. Move it to where it now belongs.
mAlarmBatches.remove(whichBatch);
addBatchLocked(mAlarmBatches, batch);
}
}
if (alarmClock != null) {
mNextAlarmClockMayChange = true;
updateNextAlarmClockLocked();
}
if (DEBUG_VALIDATE) {
if (doValidate && !validateConsistencyLocked()) {
Slog.v(TAG, "Tipping-point operation: type=" + type + " when=" + when
+ " when(hex)=" + Long.toHexString(when)
+ " whenElapsed=" + whenElapsed + " maxWhen=" + maxWhen
+ " interval=" + interval + " op=" + operation
+ " standalone=" + isStandalone);
rebatchAllAlarmsLocked(false);
}
}
rescheduleKernelAlarmsLocked();
}
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.
long nextNonWakeup = 0;
if (mAlarmBatches.size() > 0) {
final Batch firstWakeup = findFirstWakeupBatchLocked();
final Batch firstBatch = mAlarmBatches.get(0);
final Batch firstRtcWakeup = findFirstRtcWakeupBatchLocked();
// always update the kernel alarms, as a backstop against missed wakeups
if (firstWakeup != null && mNextWakeup != firstWakeup.start) {
mNextWakeup = firstWakeup.start;
setLocked(ELAPSED_REALTIME_WAKEUP, firstWakeup.start);//调用方法
}
if (firstRtcWakeup != null && mNextRtcWakeup != firstRtcWakeup.start) {
mNextRtcWakeup = firstRtcWakeup.start;
long when = firstRtcWakeup.getWhenByElapsedTime(mNextRtcWakeup);
if (when != 0) {
setLocked(RTC_POWEROFF_WAKEUP, when);
}
}
if (firstBatch != firstWakeup) {
nextNonWakeup = firstBatch.start;
}
}
if (mPendingNonWakeupAlarms.size() > 0) {
if (nextNonWakeup == 0 || mNextNonWakeupDeliveryTime < nextNonWakeup) {
nextNonWakeup = mNextNonWakeupDeliveryTime;
}
}
// always update the kernel alarm, as a backstop against missed wakeups
if (nextNonWakeup != 0) {
mNextNonWakeup = nextNonWakeup;
setLocked(ELAPSED_REALTIME, nextNonWakeup); //
}
}
private void setLocked(int type, long when) {
if (mNativeData != 0) {
// 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(mNativeData, type, alarmSeconds, alarmNanoseconds); //native JNI call,调用JN native 方法
} else {
Message msg = Message.obtain();
msg.what = ALARM_EVENT;
mHandler.removeMessages(ALARM_EVENT);
mHandler.sendMessageAtTime(msg, when);
}
}
com_android_server_AlarmManagerService.cpp
static void android_server_AlarmManagerService_set(JNIEnv*, jobject, jlong nativeData, jint type,
jlong seconds, jlong nanoseconds)
{
AlarmImpl *impl = reinterpret_cast(nativeData); //从java传递的nativeData,
//AlarmMangerService中的nativeData 实际是该JNI init() 的返回值;此时返回的是AlarmImplAlarmDriver
struct timespec ts;
ts.tv_sec = seconds;
ts.tv_nsec = nanoseconds;
int result = impl->set(type, &ts);//AlarmImplAlarmDriver->set
if (result < 0)
{
ALOGE("Unable to set alarm to %lld.%09lld: %s\n",
static_cast(seconds),
static_cast(nanoseconds), strerror(errno));
}
}
static jlong android_server_AlarmManagerService_init(JNIEnv*, jobject)
{
jlong ret = init_alarm_driver();//即操作Alarm device——"/dev/alarm"
if (ret) {
return ret;
}
return init_timerfd();//如果没有Alarm device,继续执行
}
int AlarmImplAlarmDriver::set(int type, struct timespec *ts)
{
return ioctl(fds[0], ANDROID_ALARM_SET(type), ts);// 最终ioctl操作硬件
}
最终通过ioctl的方式将时间设置给驱动,后续驱动不做详解,我也不清楚,
目的就是把时间设置给驱动,等到硬件中断返回后,再回调到native层,native再回调到framework,
如何回调到framework的呢??
我们上述说的AlarmThread,在run()方法中开启一个无限循环,循环中会调用
int result = waitForAlarm(mNativeData); //JNI 方法,使用EPOLL监听FD,阻塞等待驱动返回执行下面的分发执行;
waitForAlarm是一个native方法,具体的实现在驱动中。如果整个系统中没有alarm的时间回调,waitForAlarm则阻塞在这,直到有回调的时候才往后执行,才会继续执行上述的deliverAlarmsLocked()方法,才会去执行相应的send方法唤醒activity等。
一个完整的过程就是操作"/dev/alarm"设备的过程。自此一个完整的操作过程就结束了;