1.定义
该服务在SystemServer的其他服务中启动,是Android中系统级别的提醒服务,其主要作用是在某一个特定的时候能够唤醒设备并执行一定的操作,但是当系统关机或者重启之后则会被清除。在不同的版本上面Google做了很多功耗处理,在Android4.4以上alarm机制是非准确传递的,来最小化唤醒和电池使用,也就是使用set()和setRepeating()方法设置的闹钟则会变得不准确(Android现在批处理在合理的相似时间发生的所有应用的闹钟,以便系统仅唤醒设备一次),如果一定要准确的传递,那么可使用提供的setExact()函数;但在Android6.0以上的低电耗模式下(用户拔下充电插头并在屏幕关闭后的一段时间内使其保持不活动状态)标准的AlarmManager闹钟(包括setExact()和setWindow())将会被推迟到下一个维护阶段,如果需要在低电耗模式下触发闹钟,则使用setExactAndAllowWileIdle()或者setAndAllowWhileIdle()。系统中通过AlarmManager来管理所有的Alarm。
2.AlarmManager讲解
2.1 定义
该类中的所有实现都会调用到AlarmManagerService中,也就是外部访问AlarmManagerService的中间类。该类在SystemServiceRegistry中通过静态代码块的方式将对应的Service名称以及CachedServiceFetcher对象(用于生成对应Manager的对象)保存在HashMap中,当用户通过ContextImpl中的getSystemService()函数调用到SystemServiceRegistry中的getSystemService()函数之后,在该函数中会调用到CachedServiceFetcher对象中的getService()函数,并通过该函数生成对应的Manager实例,时序图如下图所示:
2.2 flag讲解
(1)FLAG_STANDALONE = 1;用于标识该alarm不会被加入到其他alarm集合中去(在Android4.4以上是非准确传递的,对时间相近的alarm会进行批处理),单独进行处理;
(2)FLAG_WAKE_FROM_IDLE = 2;用于标识设备即使处于idle状态,也会被唤醒处理alarm;
(3)FLAG_ALLOW_WHILE_IDLE = 4;用于标识设备即使处于idle状态也会处理alarm,并且设备不会退出idle状态;
(4)FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED = 8;类似于3,但是它的运行不会受到任何约束,仅适用于系统alarm;
(5)FLAG_IDLE_UNTIL = 16;仅适用于系统,用于告诉AlarmManager在什么时候退出idle模式,也就是只能在DeviceIdleController使用,当DeviceIdleController设置该flag的时候,说明系统已经进入到了idle状态;
(6)RTC_WAKEUP = 0;通过System.currentTimeMillis()进行时间设置,并且设备在进入到idle状态的时候设备会被唤醒;
(7)RTC = 1;通过System.currentTimeMillis()进行时间设置,并且设备在进入到idle状态的时候不会被执行;
(8)ELAPSED_REALTIME_WAKEUP = 2;通过SystemClock.elapsedRealtime()进行时间设置,并且设备在进入到idle状态的时候会被执行;
(9)ELAPSED_REALTIME = 3;通过SystemClock.elapsedRealtime()进行时间设置,并且设备在进入到idle状态的时候不会被执行。
3.AlarmManagerService讲解
3.1 通过使用AlarmManager启动Alarm首先会进入到AMS中的IBinder类中的set()函数之后,进行了如下判断操作:
(1)通过调用Alarm的应用uid判断该应用类型以此来初始化flag(用于标识该应用的alarm是否属于AlarmManager中定义的四种类型来表示alarm是否需要被特殊处理)的值;
(2) 判断传入数据的合法性:如果设置了windowLength(setWindow函数启动Alarm,用于设置Alarm可被延迟的时间)参数,如果大于了12个小时则设置为一个小时,循环唤醒的间隔时间最大值为365天最小值为1分钟,如果超过了最大值最小值的限定,则赋值为最大值或者最小值。
3.2 将绝对时间转换为相对时间进行Alarm的触发,并计算最大延迟Alarm触发时间,代码如下:
//获取从开机到当前的时间
final long nowElapsed = SystemClock.elapsedRealtime();
//如果type是RTCxx类型,则triggerAtTime -= System.currentTimeMillis() - SystemClock.elapsedRealtime();
//也就是转换为开机时间进行处理
final long nominalTrigger = convertToElapsed(triggerAtTime, type);
//最小触发时间=开机时间+5秒
final long minTrigger = nowElapsed + mConstants.MIN_FUTURITY;
//为防止alarm频繁滥发执行,最小触发Alarm的时间为5秒间隔
final long triggerElapsed = (nominalTrigger > minTrigger) ? nominalTrigger : minTrigger;
//用于计算最大触发Alarm的时间延时
final long maxElapsed;
if (windowLength == AlarmManager.WINDOW_EXACT) {
//如果alarm的时间窗为0,则表示为精确执行时间
maxElapsed = triggerElapsed;
} else if (windowLength < 0) {
//最晚触发时间
maxElapsed = maxTriggerTime(nowElapsed, triggerElapsed, interval);
windowLength = maxElapsed - triggerElapsed;
} else {
//如果时间窗口的时间大于0,则把最早触发时间+窗口时间设置为最晚触发时间
maxElapsed = triggerElapsed + windowLength;
}
复制代码
3.3 判断当前设置的alarm是否设置了FLAG_IDLE_UNTIL标签,代码如下:
//如果设置了FLAG_IDLE_UNTIL
if ((a.flags&AlarmManager.FLAG_IDLE_UNTIL) != 0) {
//判断最早可将系统从idle状态唤醒的alarm执行时间是否小于当前所设置的alarm执行时间,如果
//是则对当前alarm执行时间进行改变
if (mNextWakeFromIdle != null && a.whenElapsed > mNextWakeFromIdle.whenElapsed) {
a.when = a.whenElapsed = a.maxWhenElapsed = mNextWakeFromIdle.whenElapsed;
}
final long nowElapsed = SystemClock.elapsedRealtime();
//根据当前alarm延迟时间fuzz存在如下几个可能值:
//(1)delay < 15min,return delay;
//(2)delay < 90min,return 15min;
//(3)else return 30min
final int fuzz = fuzzForDuration(a.whenElapsed-nowElapsed);
//根据生成的fuzz产生0-fuzz范围内的随机时间并赋值
if (fuzz > 0) {
if (mRandom == null) {
mRandom = new Random();
}
final int delta = mRandom.nextInt(fuzz);
a.whenElapsed -= delta;
a.when = a.maxWhenElapsed = a.whenElapsed;
}
//如果在之前已经设置了FLAG_IDLE_UNTIL该标签的alarm,并且当前设置的alarm不存在如下几种标签,但是又需要在idle状态运
//行,那么就添加到mPendingWhileIdleAlarms延迟开始执行列表中
} else if (mPendingIdleUntil != null) {
if ((a.flags&(AlarmManager.FLAG_ALLOW_WHILE_IDLE | AlarmManager.FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED | AlarmManager.FLAG_WAKE_FROM_IDLE))
== 0) {
mPendingWhileIdleAlarms.add(a);
return;
}
}
复制代码
3.4 根据调用alarm应用的应用待机群组类别进行重新调整设置alarm的触发时间。关键代码如下:
//获取应用standbyBucket类别
final int standbyBucket = mUsageStatsManagerInternal.getAppStandbyBucket(
sourcePackage, sourceUserId, SystemClock.elapsedRealtime());
final Pair packageUser = Pair.create(sourcePackage, sourceUserId);
//获取上一次发送alarm的时间
final long lastElapsed = mLastAlarmDeliveredForPackage.getOrDefault(packageUser, 0L);
if (lastElapsed > 0) {
//下一次alarm最小触发时间
final long minElapsed = lastElapsed + getMinDelayForBucketLocked(standbyBucket);
if (alarm.expectedWhenElapsed < minElapsed) {
alarm.whenElapsed = alarm.maxWhenElapsed = minElapsed;
} else {
// app is now eligible to run alarms at the originally requested window.
// Restore original requirements in case they were changed earlier.
alarm.whenElapsed = alarm.expectedWhenElapsed;
alarm.maxWhenElapsed = alarm.expectedMaxWhenElapsed;
}
}
复制代码
应用待机群组:Android 9引入了一项新的电池管理功能,即应用待机群组。应用待机群组可以基于应用最近使用时间和使用频率帮助系统排定应用请求资源的优先级。根据该模式每个应用都会被归类到五个优先级之一,系统会根据应用所属的群组限制每个应用可以访问的设备资源。其中存在以下五个群组:
(1)活跃:用户正在使用;
(2)工作集:应用经常在运行,但是并未处于活跃状态;
(3)常用:应用会被定期使用,但不是每天都必须使用;
(4)极少使用:应用不经常使用;
(5)从未使用。
根据应用standby bucket类型应用alarm依次被延迟0min,6min,30min,2h,10d。
3.5 将Alarm加入到batch中
在Android4.4以上,Alarm默认的工作方式是非精准模式,除非采用精准模式。而在非精准模式下面Alarm都是批量处理的,每个Alarm根据触发时间以及最大触发时间会被加入到不同的batch中,同一个batch中的不同Alarm是同时发生的。代码如下:
//如果当前alarm的触发时间不受其他影响则返回-1,否则从列表中获取
int whichBatch = ((alarm.flags & AlarmManager.FLAG_STANDALONE) != 0) ? -1
: attemptCoalesceLocked(alarm.whenElapsed, alarm.maxWhenElapsed);
if (whichBatch < 0) {
//将该alarm构造为一个batch并通过二分查找法查到到需要被添加到的下标,并添加到列表中去(列表中的alarm是按照alarm
//的触发时间从列表下标0开始递增添加的)
addBatchLocked(mAlarmBatches, new Batch(alarm));
} else {
final Batch batch = mAlarmBatches.get(whichBatch);
if (batch.add(alarm)) {
//最新添加进入的alarm触发时间不同,所以需要remove之后重新添加进去,以改变其在列表中的index
mAlarmBatches.remove(whichBatch);
addBatchLocked(mAlarmBatches, batch);
}
}
//获取在列表中的batch,如果没有查找到则返回-1
int attemptCoalesceLocked(long whenElapsed, long maxWhen) {
final int N = mAlarmBatches.size();
for (int i = 0; i < N; i++) {
Batch b = mAlarmBatches.get(i);
//如果需要被添加进入的alarm的触发时间和最大触发时间与该batch的开始触发时间和结束触发时间有重合部分则返回该下标
if ((b.flags&AlarmManager.FLAG_STANDALONE) == 0 && b.canHold(whenElapsed, maxWhen)) {
return i;
}
}
return -1;
}
//判断被添加的alarm的触发时间和最大触发时间间隔是否和当前batch有重合部分
boolean canHold(long whenElapsed, long maxWhen) {
return (end >= whenElapsed) && (start <= maxWhen);
}
复制代码
3.6 在以下两种情况下所有的alarm会被重新rebatch:
- 当前alarm设置了FLAG_IDLE_UNTIL标签,用于标识系统在什么时候退出idle状态;
- 如果当前alarm设置了FLAG_WAKE_FROM_IDLE标签,表示该alarm可以将系统从idle状态唤醒,并且mNextWakeFromIdle==null||mNextWakeFromIdle.whenElapsed > a.whenElapsed,则将当前alarm设置为mNextWakeFromIdle,并且mPendingIdleUntil != null则对所有的alarm进行rebatch处理。
3.7 rescheduleKernelAlarmsLocked,设置第一个唤醒类alarm和非唤醒类alarm执行时间到kernel层,代码如下:
long nextNonWakeup = 0;
if (mAlarmBatches.size() > 0) {
//获取第一个可以将系统从idle状态唤醒执行的alarm集合(batch)
final Batch firstWakeup = findFirstWakeupBatchLocked();
//获取列表中第一个batch
final Batch firstBatch = mAlarmBatches.get(0);
if (firstWakeup != null) {
//获取batch中alarm的最早执行时间
mNextWakeup = firstWakeup.start;
mLastWakeupSet = SystemClock.elapsedRealtime();
//将最早唤醒类alarm执行时间设置到kernel层
setLocked(ELAPSED_REALTIME_WAKEUP, firstWakeup.start);
}
//如果第一个batch集合中的alarm不是唤醒类alarm,则把mAlarmBatches列表中的
//第一个批处理对象的start赋值给nextNonWakeup
if (firstBatch != firstWakeup) {
nextNonWakeup = firstBatch.start;
}
}
if (mPendingNonWakeupAlarms.size() > 0) {
//如果下一个非唤醒类alarm执行时间小于了当前获取到的时间,则重新赋值
if (nextNonWakeup == 0 || mNextNonWakeupDeliveryTime < nextNonWakeup) {
nextNonWakeup = mNextNonWakeupDeliveryTime;
}
}
//重新更新mNextNonWakeup的值,并将最早非唤醒类alarm执行时间设置到kernel层
if (nextNonWakeup != 0 && mNextNonWakeup != nextNonWakeup) {
mNextNonWakeup = nextNonWakeup;
setLocked(ELAPSED_REALTIME, nextNonWakeup);
}
//获取第一个可以唤醒系统类的alarm,其中TYPE_NONWAKEUP_MASK = 1,而type = 0或者3时不能唤醒系统
boolean hasWakeups() {
final int N = alarms.size();
for (int i = 0; i < N; i++) {
Alarm a = alarms.get(i);
if ((a.type & TYPE_NONWAKEUP_MASK) == 0) {
return true;
}
}
return false;
}
复制代码
3.8 updateNextAlarmClockLocked,计算设置了alarmClock的Alarm执行顺序,部分代码如下:
private void updateNextAlarmClockLocked() {
if (!mNextAlarmClockMayChange) {
return;
}
...........
final int N = mAlarmBatches.size();
for (int i = 0; i < N; i++) {
ArrayList alarms = mAlarmBatches.get(i).alarms;
final int M = alarms.size();
for (int j = 0; j < M; j++) {
Alarm a = alarms.get(j);
//判断该alarm中是否设置了alarmClock
if (a.alarmClock != null) {
//获取设置该alarm应用的uid
final int userId = UserHandle.getUserId(a.uid);
//根据uid从mNextAlarmClockForUser中获取对应的alarmClock
AlarmManager.AlarmClockInfo current = mNextAlarmClockForUser.get(userId);
//由于在存储alarm的时候是按时间从小到大依次存储,而读取也是按照顺序读取,所以可以直接进行存储
if (nextForUser.get(userId) == null) {
nextForUser.put(userId, a.alarmClock);
//如果该alarm存在于nextForUser列表中并且也存在于mNextAlarmClockForUser列表中,根据时间进
//行判断是否更新nextForUser列表
} else if (a.alarmClock.equals(current)
&& current.getTriggerTime() <= nextForUser.get(userId).getTriggerTime()) {
nextForUser.put(userId, current);
}
}
}
}
//mNextAlarmClockForUser中的数据是根据nextForUser中的数据进行增加或者删除
final int NN = nextForUser.size();
for (int i = 0; i < NN; i++) {
AlarmManager.AlarmClockInfo newAlarm = nextForUser.valueAt(i);
int userId = nextForUser.keyAt(i);
AlarmManager.AlarmClockInfo currentAlarm = mNextAlarmClockForUser.get(userId);
//如果nextForUser和mNextAlarmClockForUser列表中同一个uid对应的alarmClock不同,则将
//nextForUser中的AlarmClock存储到mNextAlarmClockForUser中
if (!newAlarm.equals(currentAlarm)) {
updateNextAlarmInfoForUserLocked(userId, newAlarm);
}
}
final int NNN = mNextAlarmClockForUser.size();
for (int i = NNN - 1; i >= 0; i--) {
int userId = mNextAlarmClockForUser.keyAt(i);
//如果mNextAlarmClockForUser中uid在nextForUser中alarmClock值不存在,则从mNextAlarmClockForUser
//列表中删除该uid所对应的item
if (nextForUser.get(userId) == null) {
updateNextAlarmInfoForUserLocked(userId, null);
}
}
}
复制代码
通过调用updateNextAlarmInfoForUserLocked函数对mNextAlarmClockForUser列表中的数据进行更新,最后调用到sendNextAlarmClockChanged函数中更新系统设置中NEXT_ALARM_FORMATTED的值,并发送NEXT_ALARM_CLOCK_CHANGED_INTENT广播进行通知。
注:SystemClock.elapsedRealtime()是从开机到现在的时间包括睡眠的时间,SystemClock.uptimeClock()是从开机到现在的时间但是不包括睡眠的时间。
4.AlarmManagerService启动流程
4.1 AMS在SystemServer中启动,时序图如下:
4.2 在onStart()方法中主要是做一系列的初始化操作,具体代码如下: @Override
public void onStart() {
mNativeData = init();
mNextWakeup = mNextRtcWakeup = mNextNonWakeup = 0;
//向存储关机闹钟文件中存入初始值0
AlarmManager.writePowerOffAlarmFile(AlarmManager.POWER_OFF_ALARM_SET_FILE,
AlarmManager.POWER_OFF_ALARM_NOT_SET);
//Android系统在首次开机或者进行恢复出厂设置的时候首先会进行全盘加密即FDE操作,在这个过程中vold会将vold.decrypt设为
//trigger_restart_min_framework,装载tmpfs临时分区,并且会重启main组内的服务,当加密成功后会装载/data分区。然后vol
//d会将vold.decrypt属性设置为trigger_restart_framework。
String cryptState = SystemProperties.get("vold.decrypt");
if (ENCRYPTING_STATE.equals(cryptState) || ENCRYPTED_STATE.equals(cryptState)) {
mIsEncryptStatus = true;
}
//将当前时区信息设置到kernel层中,如果时区发生了变化则发送ACTION_TIMEZONE_CHANGED广播
if (mIsEncryptStatus) {
String tz = AlarmManager.readPowerOffAlarmFile(AlarmManager.POWER_OFF_ALARM_TIMEZONE_FILE);
setTimeZoneImpl(tz);
} else {
setTimeZoneImpl(SystemProperties.get(TIMEZONE_PROPERTY));
}
// 设置系统时间
if (mNativeData != 0) {
final long systemBuildTime = Environment.getRootDirectory().lastModified();
if (System.currentTimeMillis() < systemBuildTime) {
Slog.i(TAG, "Current time only " + System.currentTimeMillis()
+ ", advancing to build time " + systemBuildTime);
setKernelTime(mNativeData, systemBuildTime);
}
}
//获取systemUI的uid值,用于判断调用alarm的应用是否是systemui
final PackageManager packMan = getContext().getPackageManager();
try {
PermissionInfo sysUiPerm = packMan.getPermissionInfo(SYSTEM_UI_SELF_PERMISSION, 0);
ApplicationInfo sysUi = packMan.getApplicationInfo(sysUiPerm.packageName, 0);
if ((sysUi.privateFlags&ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0) {
mSystemUiUid = sysUi.uid;
} else {
Slog.e(TAG, "SysUI permission " + SYSTEM_UI_SELF_PERMISSION
+ " defined by non-privileged app " + sysUi.packageName
+ " - ignoring");
}
} catch (NameNotFoundException e) {
}
if (mSystemUiUid <= 0) {
Slog.wtf(TAG, "SysUI package not found!");
}
//生成一个新的wakelock
PowerManager pm = (PowerManager) getContext().getSystemService(Context.POWER_SERVICE);
mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "*alarm*");
//构造时间变化的PendingIntent
mTimeTickSender = PendingIntent.getBroadcastAsUser(getContext(), 0,
new Intent(Intent.ACTION_TIME_TICK).addFlags(
Intent.FLAG_RECEIVER_REGISTERED_ONLY
| Intent.FLAG_RECEIVER_FOREGROUND
| Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS), 0,
UserHandle.ALL);
//初始化日期变化广播发送者
Intent intent = new Intent(Intent.ACTION_DATE_CHANGED);
intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING
| Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS);
mDateChangeSender = PendingIntent.getBroadcastAsUser(getContext(), 0, intent,
Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT, UserHandle.ALL);
//初始化接收时间变化以及日期变化广播接受者
mClockReceiver = new ClockReceiver();
//设置alarm,用于在一分钟之后发送时间变化广播
mClockReceiver.scheduleTimeTickEvent();
//设置alarm,用于在一天之后发送日期变化广播
mClockReceiver.scheduleDateChangedEvent();
//初始化亮灭屏广播接收者
mInteractiveStateReceiver = new InteractiveStateReceiver();
//初始化应用卸载广播接收
mUninstallReceiver = new UninstallReceiver();
//初始化AlarmThread并在该线程中开启一个死循环用于读取alarm并执行
if (mNativeData != 0) {
AlarmThread waitThread = new AlarmThread();
waitThread.start();
} else {
Slog.w(TAG, "Failed to open alarm driver. Falling back to a handler.");
}
//用于注册监听uid状态值变化
try {
ActivityManager.getService().registerUidObserver(new UidObserver(),
ActivityManager.UID_OBSERVER_GONE | ActivityManager.UID_OBSERVER_IDLE
| ActivityManager.UID_OBSERVER_ACTIVE,
ActivityManager.PROCESS_STATE_UNKNOWN, null);
} catch (RemoteException e) {
// ignored; both services live in system_server
}
publishBinderService(Context.ALARM_SERVICE, mService);
}
复制代码
4.3 AlarmManagerService中通过在线程AlarmThread中启动一个无线循环并进行阻塞,当底层在某一个时间点触发执行alarm之后则继续往下执行,代码如下:
private class AlarmThread extends Thread{
public AlarmThread(){
super("AlarmManager");
}
public void run(){
ArrayList triggerList = new ArrayList();
while (true){
//阻塞方法,当底层触发alarm之后继续执行
int result = waitForAlarm(mNativeData);
//当前绝对时间
final long nowRTC = System.currentTimeMillis();
//当前开机时间,包含休眠的时间
final long nowELAPSED = SystemClock.elapsedRealtime();
synchronized (mLock) {
//记录当前系统唤醒时间
mLastWakeup = nowELAPSED;
}
triggerList.clear();
if ((result & TIME_CHANGED_MASK) != 0) {
//由于内核内部微小的调整可能导致内核给我们发送虚拟的时间更改通知,因此这里需要进行过滤
final long lastTimeChangeClockTime;
final long expectedClockTime;
synchronized (mLock) {
//上一次执行alarm的绝对时间
lastTimeChangeClockTime = mLastTimeChangeClockTime;
//上一次执行alarm的绝对时间 + 当前执行alarm的开机时间-上一次执行alarm的开机时间=
//当前期望执行alarm的绝对时间
expectedClockTime = lastTimeChangeClockTime
+ (nowELAPSED - mLastTimeChangeRealtime);
}
/判断第一次或者期望执行alarm的绝对时间和当前绝对时间相差大于了 +- 1s
if (lastTimeChangeClockTime == 0 || nowRTC < (expectedClockTime-1000)
|| nowRTC > (expectedClockTime+1000)) {
//由于时间变化,重新批处理所有的alarm
removeImpl(mTimeTickSender);
removeImpl(mDateChangeSender);
rebatchAllAlarms();
mClockReceiver.scheduleTimeTickEvent();
mClockReceiver.scheduleDateChangedEvent();
synchronized (mLock) {
mNumTimeChanged++;
//记录当前执行alarm的绝对时间和开机时间
mLastTimeChangeClockTime = nowRTC;
mLastTimeChangeRealtime = nowELAPSED;
}
//发送时间变化广播
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_INCLUDE_BACKGROUND
| Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS);
getContext().sendBroadcastAsUser(intent, UserHandle.ALL);
// The world has changed on us, so we need to re-evaluate alarms
// regardless of whether the kernel has told us one went off.
result |= IS_WAKEUP_MASK;
}
}
if (result != TIME_CHANGED_MASK) {
synchronized (mLock) {
mLastTrigger = nowELAPSED;
//查找列表中是否存在可以将系统从idle状态唤醒的alarm
boolean hasWakeup = triggerAlarmsLocked(triggerList, nowELAPSED, nowRTC);
//判断非唤醒类alarm是否满足被延迟执行的条件
if (!hasWakeup && checkAllowNonWakeupDelayLocked(nowELAPSED)) {
//记录开始延迟时间以及计算下一次alarm发送时间
if (mPendingNonWakeupAlarms.size() == 0) {
mStartCurrentDelayTime = nowELAPSED;
mNextNonWakeupDeliveryTime = nowELAPSED
+ ((currentNonWakeupFuzzLocked(nowELAPSED)*3)/2);
}
mPendingNonWakeupAlarms.addAll(triggerList);
mNumDelayedAlarms += triggerList.size();
//因为列表数据发送的变化所以需要更新内核执行alarm的时间点以及下一次执行的alarmClock
rescheduleKernelAlarmsLocked();
updateNextAlarmClockLocked();
} else {
if (mPendingNonWakeupAlarms.size() > 0) {
//计算非唤醒类alarm发送优先级
calculateDeliveryPriorities(mPendingNonWakeupAlarms);
triggerList.addAll(mPendingNonWakeupAlarms);
//根据优先级重新对alarm进行排序
Collections.sort(triggerList, mAlarmDispatchComparator);
//alarm发送的延迟时间
final long thisDelayTime = nowELAPSED - mStartCurrentDelayTime;
mTotalDelayTime += thisDelayTime;
if (mMaxDelayTime < thisDelayTime) {
mMaxDelayTime = thisDelayTime;
}
mPendingNonWakeupAlarms.clear();
}
final ArraySet> triggerPackages =
new ArraySet<>();
for (int i = 0; i < triggerList.size(); i++) {
final Alarm a = triggerList.get(i);
//如果a.alarmClock != null || 是核心应用 || flag & FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED
//!= 0
if (!isExemptFromAppStandby(a)) {
triggerPackages.add(Pair.create(
a.sourcePackage, UserHandle.getUserId(a.creatorUid)));
}
}
//发送alarm
deliverAlarmsLocked(triggerList, nowELAPSED);
reorderAlarmsBasedOnStandbyBuckets(triggerPackages);
//更新kernel层下一次执行alarm的时间点以及下一次执行的alarmClock
rescheduleKernelAlarmsLocked();
updateNextAlarmClockLocked();
}
}
} else {
// Just in case -- even though no wakeup flag was set, make sure
// we have updated the kernel to the next alarm time.
synchronized (mLock) {
rescheduleKernelAlarmsLocked();
}
}
}
}
}
boolean checkAllowNonWakeupDelayLocked(long nowELAPSED) {
//是否是亮屏
if (mInteractive) {
return false;
}
//上一次传递alarm的时间是否小于等于0
if (mLastAlarmDeliveryTime <= 0) {
return false;
}
//如果当前传递alarm的时间小于当前时间,那么就返回false
if (mPendingNonWakeupAlarms.size() > 0 && mNextNonWakeupDeliveryTime < nowELAPSED) {
// This is just a little paranoia, if somehow we have pending non-wakeup alarms
// and the next delivery time is in the past, then just deliver them all. This
// avoids bugs where we get stuck in a loop trying to poll for alarms.
return false;
}
//上一次发送alarm距离当前的时间
long timeSinceLast = nowELAPSED - mLastAlarmDeliveryTime;
return timeSinceLast <= currentNonWakeupFuzzLocked(nowELAPSED);
}
long currentNonWakeupFuzzLocked(long nowELAPSED) {
//息屏持续时间
long timeSinceOn = nowELAPSED - mNonInteractiveStartTime;
if (timeSinceOn < 5*60*1000) {
return 2*60*1000;
} else if (timeSinceOn < 30*60*1000) {
return 15*60*1000;
} else {
return 60*60*1000;
}
}
复制代码
4.3.1 triggerAlarmsLocked,查找alarm列表中是否存在可以将系统从idle状态唤醒的alarm,如果存在则返回true;并获取alarm的触发列表,代码如下所示:
boolean triggerAlarmsLocked(ArrayList triggerList, final long nowELAPSED,final long nowRTC) {
boolean hasWakeup = false;
//在列表中我们是安装时间从小到大的顺序存储的,所以我们只需要按照列表中的顺序进行读取
while (mAlarmBatches.size() > 0) {
Batch batch = mAlarmBatches.get(0);
//如果第一个batch列表中的第一个alarm开始执行时间大于的当前时间,那么直接退出当前循环(说明所有alarm
的执行时间都没有达到执行时间)
if (batch.start > nowELAPSED){
break;
}
//将当前被处理的批处理对象从列表中移除,以防止其他地方访问该对象
mAlarmBatches.remove(0);
final int N = batch.size();
for (int i = 0; i < N; i++) {
Alarm alarm = batch.get(i);
//如果当前alarm可以在系统处于idle状态的时候被执行,则需要限制app调用这些alarm的频率,也就是增加
//执行间隔时间
if ((alarm.flags&AlarmManager.FLAG_ALLOW_WHILE_IDLE) != 0) {
//获取alarm上一次执行时间
final long lastTime = mLastAllowWhileIdleDispatch.get(alarm.creatorUid, -1);
//获取alarm下一次最早执行时间
final long minTime = lastTime + getWhileIdleMinIntervalLocked(alarm.creatorUid);
//如果上一次执行时间大于0且当前时间小于了该alarm距离上一次执行时间的最小执行时间
//则重新设置该alarm的执行时间
if (lastTime >= 0 && nowELAPSED < minTime) {
alarm.expectedWhenElapsed = alarm.whenElapsed = minTime;
if (alarm.maxWhenElapsed < minTime) {
alarm.maxWhenElapsed = minTime;
}
alarm.expectedMaxWhenElapsed = alarm.maxWhenElapsed;
........
//由于alarm的变量发生了变化,所以需要重新存储到对应的batch队列中
setImplLocked(alarm, true, false);
continue;
}
}
........
alarm.count = 1;
//存储可被触发的alarm
triggerList.add(alarm);
........
if (mPendingIdleUntil == alarm) {
//当前alarm是mPendingIdleUntil,则把mPendingIdleUntil置为null,并对所有alarm进行重新存储
mPendingIdleUntil = null;
rebatchAllAlarmsLocked(false);
restorePendingWhileIdleAlarmsLocked();
}
if (mNextWakeFromIdle == alarm) {
mNextWakeFromIdle = null;
rebatchAllAlarmsLocked(false);
}
//如果当前alarm可以重复执行
if (alarm.repeatInterval > 0) {
//计算alarm从最开始执行到当前时间的执行次数
alarm.count += (nowELAPSED - alarm.expectedWhenElapsed) / alarm.repeatInterval;
// 计算alarm的执行时间间隔
final long delta = alarm.count * alarm.repeatInterval;
//计算alarm的下一次执行时间
long value1 = alarm.whenElapsed + delta;
long value2 = nowELAPSED + alarm.repeatInterval;
final long nextElapsed = value1 > value2 ? value1 : value2;
//将alarm下一次的执行添加到alarm列表中
setImplLocked(alarm.type, alarm.when + delta, nextElapsed, alarm.windowLength,
maxTriggerTime(nowELAPSED, nextElapsed, alarm.repeatInterval),
alarm.repeatInterval, alarm.operation, null, null, alarm.flags, true,
alarm.workSource, alarm.alarmClock, alarm.uid, alarm.packageName);
}
//判断当前alarm是否可以将系统从idle状态唤醒
if (alarm.wakeup) {
hasWakeup = true;
}
//用于更新下一次执行带有alarmClock的alarm
if (alarm.alarmClock != null) {
mNextAlarmClockMayChange = true;
}
}
}
// This is a new alarm delivery set; bump the sequence number to indicate that
// all apps' alarm delivery classes should be recalculated.
mCurrentSeq++;
//重新计算alarm的传递优先级
calculateDeliveryPriorities(triggerList);
//根据优先级进行排序
Collections.sort(triggerList, mAlarmDispatchComparator);
........
return hasWakeup;
}
复制代码
5.总结
设置alarm到AlarmManagerService中流程:
(1)用户通过AlarmManager设置alarm到AlarmManagerService中;
(2)AlarmManager中都会调用到setImpl()方法中;
(3)通过setImpl()方法调用到AlamManagerService中IBinder类的set()方法中;
(4)根据应用类型初始化flag值,用于后续判断该应用所设置的alarm的处理方式(是否可以唤醒系统执行alarm、是否可以在idle状态执行alarm等);
(5)调用setImpl()方法用于将用户传入的参数合法化,并将绝对时间转换为系统开机时间进行处理;
(6)构造新的alarm,如果在列表中存在该类型alarm,则通过传入的PendingIntent、AlarmListener进行remove;
(7)如果当前alarm的flag&FLAG_IDLE_UNTIL != 0,那么将当前Alarm执行时间提前。else mPendingIdleUntil != null且flag & (FLAG_ALLOW_WHILE_IDLE | FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED | FLAG_WAKE_FROM_IDLE)== 0则将当前alarm添加到 mPendingWhileIdleAlarms中;
(8)根据应用的standby bucket以及上一次发送时间调整alarm的触发时间;
(9)将根据alarm是否设置了FLAG_STANDALONE添加到对应的Batch中,并将batch按照batch中start时间从小到大的顺序添加到list中;
(10)如果 mPendingIdleUntil和 mNextWakeFromIdle改变了那么对列表中的alarm进行rebatch;
(11)重新计算触发唤醒类batch的start时间和非唤醒类batch的start时间到kernel层;
(12)更新下一个被执行的alarm Clock到设置中,并发送NEXT_ALARM_CLOCK_CHANGED_INTENT广播给所有用户;
alarm触发流程讲解:
(1)AlarmThread中监听到kernel层触发执行alarm;
(2)如果返回的result中存在TIME_CHANGED_MASK(时间改变)标签,那么对所有的alarm进行rebatch操作并发送ACTION_TIME_CHANGED广播;
(3)判断alarm中是否存在可以唤醒系统类型,如果不存在则将所有获取需要被执行的alarm添加到mPendingNonWakeupAlarms列表中,并对数据进行更新;
(4)如果存在,则根据获取到的alarm列表发送alarm,最后再更新下一次触发alarm的时间以及下一次需要执行的alarm Clock。
整个流程也可以理解为:用户调用设置Alarm方法到AlarmManagerService中,根据用户设置Alarm的最早触发时间和最晚触发时间或者flag添加到对应的batch对象中,然后更新Alarm的触发时间到kernel层;当AlarmThread线程监听到kernel层触发执行Alarm的返回结果,根据判断的结果发送Alarm,也就是Alarm的PendingIntent。
参考链接:
blog.csdn.net/singwhatiwa…
blog.csdn.net/zhangyongfe…