MTK 6.0 Alarm机制分析
(MTK Android6.0)
一、概述
Alarm模块处于Android Framework层,是Android中常用的一种系统服务级别的提示服务,在指定时间或周期性启动其他组件(Activity、Service、BroadcastReceiver),Alarm系统架构如下图所示:
Alarm系统架构
二、AlarmManager介绍
Alarm对外通过AlarmManager提供接口给应用程序开发者使用,
主要提供如下接口:
(1)set(int type,long startTime,PendingIntent pi);
该方法用于设置一次性闹钟,第一个参数表示闹钟类型,第二个参数表示闹钟执行时间,第三个参数表示闹钟响应动作。
(2)setRepeating(int type,long startTime,long intervalTime,PendingIntent pi);
该方法用于设置重复闹钟,第一个参数表示闹钟类型,第二个参数表示闹钟首次执行时间,第三个参数表示闹钟两次执行的间隔时间,第三个参数表示闹钟响应动作。
(3)setInexactRepeating(int type,long startTime,long intervalTime,PendingIntent pi);
该方法也用于设置重复闹钟,与第二个方法相似,不过其两个闹钟执行的间隔时间不是固定的而已。
(4) setExact(int type, long triggerAtMillis,PendingIntent operation)
该方法用于设置一次性精确闹钟
(5)setAndAllowWhileIdle(int type, longtriggerAtMillis, PendingIntent operation)
该方法用于设置一次性闹钟,但该闹钟运行在系统空闲状态下触发
(6)setExactAndAllowWhileIdle(int type, longtriggerAtMillis, PendingIntent operation)
该方法用于设置一次性精确闹钟,但该闹钟运行在系统空闲状态下触发
主要有四种类型的闹钟:
AlarmManager.ELAPSED_REALTIME表示闹钟在手机睡眠状态下不可用,该状态下闹钟使用相对时间(相对于系统启动开始),状态值为3;
AlarmManager.ELAPSED_REALTIME_WAKEUP表示闹钟在睡眠状态下会唤醒系统并执行提示功能,该状态下闹钟也使用相对时间,状态值为2;
AlarmManager.RTC表示闹钟在睡眠状态下不可用,该状态下闹钟使用绝对时间,即当前系统时间,状态值为1;
AlarmManager.RTC_WAKEUP表示闹钟在睡眠状态下会唤醒系统并执行提示功能,该状态下闹钟使用绝对时间,状态值为0;
三、AlarmManagerService介绍
AlarmManagerService(后面简称ALMS)是Alarm模块的系统服务,主要逻辑也是在该服务中完成, ALMS的启动有系统进程完成。有SystemServer进程调用startOtherServices()
来启动:
[->SystemServer.java]
private void startOtherServices() {
...
mSystemServiceManager.startService(AlarmManagerService.class);
...
}
下面以app调用setExactAndAllowWhileIdle()接口为例介绍ALMS的机制,调用流程的时序图如下:
ALMS时序图
(1) 系统进程在启动ALMS服务时先实例化ALMS,同时初始化Handler和常量Constants类
final AlarmHandler mHandler = new AlarmHandler();
final Constants mConstants;
public AlarmManagerService(Context context) {
super(context);
// 初始化Handler和常量Constants类
mConstants = new Constants(mHandler);
}
(2) SystemService启动ALMS过程中调用ALMS 的onStart()方法,主要完成初始化alarm的驱动。添加时间变化闹钟和日志变化闹钟并启动Alarm的监听线程。
(3) 通过JNI对mNativeData进行初始化操作,打开设备驱动"/dev/alarm"返回一个long型的与fd文件描述符有关的值
(4) 初始化MTK 省电模块
(5) 初始化AlarmThread类,该类主要监听Alarm驱动的触发信息,完成定时触发的功能
(6) 启动Alarm监听线程
(7) 通过调用jni方法waitForAlarm(mNativeData)来监听触发信息,当驱动没有触发信息时,线程将进入等待状态直到有触发信息。到此ALMS的启动过程就完成了。
(8) APP若需要创建一个Alarm则可以通过调用AlarmManager提供的接口来完成,此处我们创建一个精准并且在系统进入”休闲”状态也能触发的闹钟。代码如下:
AlarmManager manager =(AlarmManager)getSystemService(Context.ALARM_SERVICE);
Intent intent=new Intent(INTENT_ACTION);
PendingIntentpendingIntent=PendingIntent.getBroadcast(context, 0,intent,PendingIntent.FLAG_UPDATE_CURRENT);
long firstime= SystemClock.elapsedRealtime();//开始时间
aManager.setExactAndAllowWhileIdle(AlarmManager.ELAPSED_REALTIME_WAKEUP,firstime+(intervalMillis*1000) , pendingIntent);
(9) 通过Binder IPC 调用到ALMS服务端的set接口,在ALMS中进一步调用setImpl()接口,此接口可以修正传递过来的PendingIntent各相关参数:
检查时间窗长度,如果超过半天就转换成一小时。
检查alarm的执行周期,如果alarm的执行周期小于1分钟,则转换为1分钟。从这里可以得知周期性alarm的最小有效周期为1分钟,若要执行周期小于1分钟的事,不能使用alarm实现。
检查触发时间,如果触发时间小于5秒,就将触发时间设置为5秒,为了防止alarm频繁滥发执行。
(10) 调用mtk的省电模块,获取maxElapsed时间,判断流程如下:
当满足如下条件时maxElapsed值设置为触发时间+5分钟:
background_power_saving_enable值为0
WIFI连接显示设备
Usb处于连接状态
亮屏
息屏且息屏时间小于1分钟
Type不为RTC_WAKEUP和ELAPSED_REALTIME_WAKEUP
白名单中配置的app
系统app且包名为com.android的app
(11) 创建Alarm对象,即一个闹钟。初始化类型、触发时间等信息
(12) 查找mAlarmBatches中是否有一样的Alarm,有则移除
(13) 判断该Alarm的触发时间范围是否满足现有的Batch中,判断依据如下:
当Alarm满足以上四个条件时,Alarm就属于对应的Batch中,即需要添加对应Batch中作为批量触发中的一个。此时whichBatch为对应的Batch索引值
当Alarm不满足以上四个条件时,whichBatch的值为小于0,即需要重新创建一个Batch对象。
(14) 创建Batch对象,并将Alarm的whenElapsed值赋值给start maxWhenElapsed值赋值给end当是将Alarm插入现有的Batch对象中时,Batch的start和end时间需要根据插入的Alarm的whenElapsed和maxWhenElapsed值来做调整。即:
if (alarm.whenElapsed > start){
start = alarm.whenElapsed;
}
if (alarm.maxWhenElapsed < end) {
end= alarm.maxWhenElapsed;
}
(15) 添加Batch对象到mAlarmBatches数组链表中,mAlarmBatches保存着系统中所有的Alarm。其保存机制如下图:
保存在mAlarmBatches变量中的Batch对象先按照时间升序排序。在Batch类对象中维护了一个保存Alarm的alarms对象,alarms对象中的Alarm也是按照时间升序排序。
(16) 获取mAlarmBatches中第一个wakeup类型的Batch对象的start时间。并将触发时间设置到驱动中
(17) 调用jni方法 set()设置alarm,驱动的alarm只跟类型、触发时间相关,跟alarm的其他参数无关。
(18) 更新下一个Alarm的alarmClock
(19) 当设置给驱动的Alarm的触发时间到了时,驱动将唤醒AlarmThread等待线程,并通过返回值result携带相应信息给ALMS。线程唤醒后通过调用triggerAlarmsLocked()方法获取满足触发条件的Alarm并保存在triggerList数组链表中。
(20) 执行triggerList中的alarm 并执行执行和PendingIntent关联的操作 ,即启动其他组件 (如:Activity、Service、BroadcastReceiver)。