Android6.0 设备Idle状态(二)AlarmManagerService setIdleUntil接口
来源:互联网 发布:数据漫游用打开吗 编辑:程序博客网 时间:2024/05/18 01:27
在上一篇博客中的DeviceIdleController中讲到了AlarmManager的setIdelUntil接口,设置后除非是白名单中的应用,其他应用设置Alarm都会无效。
我们今天就来分析下这个接口。
一、set接口对flag中带了FLAG_IDLE_UNTIL的Alarm处理
先来看AlarmManager中:
public void setIdleUntil(int type, long triggerAtMillis, PendingIntent operation) { setImpl(type, triggerAtMillis, WINDOW_EXACT, 0, FLAG_IDLE_UNTIL, operation, null, null); }
这个接口就是在flag中带了FLAG_IDLE_UNTIL。
我们再来看下AlarmManagerService中的set函数:
private final IBinder mService = new IAlarmManager.Stub() { @Override public void set(int type, long triggerAtTime, long windowLength, long interval, int flags, PendingIntent operation, WorkSource workSource, AlarmManager.AlarmClockInfo alarmClock) { final int callingUid = Binder.getCallingUid(); if (workSource != null) { getContext().enforcePermission( android.Manifest.permission.UPDATE_DEVICE_STATS, Binder.getCallingPid(), callingUid, "AlarmManager.set"); } // No incoming callers can request either WAKE_FROM_IDLE or // ALLOW_WHILE_IDLE_UNRESTRICTED -- we will apply those later as appropriate. flags &= ~(AlarmManager.FLAG_WAKE_FROM_IDLE | AlarmManager.FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED); // Only the system can use FLAG_IDLE_UNTIL -- this is used to tell the alarm // manager when to come out of idle mode, which is only for DeviceIdleController. if (callingUid != Process.SYSTEM_UID) {//如果不是系统应用不能拥有该flag flags &= ~AlarmManager.FLAG_IDLE_UNTIL; }
不是系统应用不能应有该flag。注释的说明也是这个flag只给DeviceIdleController用。
二、setImplLocked函数
继续分析setImplLocked函数
private void setImplLocked(Alarm a, boolean rebatching, boolean doValidate) { if ((a.flags&AlarmManager.FLAG_IDLE_UNTIL) != 0) {//是该flag // This is a special alarm that will put the system into idle until it goes off. // The caller has given the time they want this to happen at, however we need // to pull that earlier if there are existing alarms that have requested to // bring us out of idle. if (mNextWakeFromIdle != null) {//第一次该值为null a.when = a.whenElapsed = a.maxWhenElapsed = mNextWakeFromIdle.whenElapsed; } // Add fuzz to make the alarm go off some time before the actual desired time. final long nowElapsed = SystemClock.elapsedRealtime(); final int fuzz = fuzzForDuration(a.whenElapsed-nowElapsed);// 调整时间 if (fuzz > 0) { if (mRandom == null) { mRandom = new Random(); } final int delta = mRandom.nextInt(fuzz); a.whenElapsed -= delta;//调整时间 if (false) { Slog.d(TAG, "Alarm when: " + a.whenElapsed); Slog.d(TAG, "Delta until alarm: " + (a.whenElapsed-nowElapsed)); Slog.d(TAG, "Applied fuzz: " + fuzz); Slog.d(TAG, "Final delta: " + delta); Slog.d(TAG, "Final when: " + a.whenElapsed); } a.when = a.maxWhenElapsed = a.whenElapsed; } } else if (mPendingIdleUntil != null) { // We currently have an idle until alarm scheduled; if the new alarm has // not explicitly stated it wants to run while idle, then put it on hold. if ((a.flags&(AlarmManager.FLAG_ALLOW_WHILE_IDLE | AlarmManager.FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED | AlarmManager.FLAG_WAKE_FROM_IDLE)) == 0) { mPendingWhileIdleAlarms.add(a); return; } } int whichBatch = ((a.flags&AlarmManager.FLAG_STANDALONE) != 0)//这块和以前一样没改动 ? -1 : attemptCoalesceLocked(a.whenElapsed, a.maxWhenElapsed); if (whichBatch < 0) { Batch batch = new Batch(a); 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 (a.alarmClock != null) { mNextAlarmClockMayChange = true; } boolean needRebatch = false; if ((a.flags&AlarmManager.FLAG_IDLE_UNTIL) != 0) { mPendingIdleUntil = a;//如果又改flag的alarm,设置mPendingIdleUntil mConstants.updateAllowWhileIdleMinTimeLocked(); needRebatch = true;// 需要rebatch } else if ((a.flags&AlarmManager.FLAG_WAKE_FROM_IDLE) != 0) { if (mNextWakeFromIdle == null || mNextWakeFromIdle.whenElapsed > a.whenElapsed) { mNextWakeFromIdle = a; // If this wake from idle is earlier than whatever was previously scheduled, // and we are currently idling, then we need to rebatch alarms in case the idle // until time needs to be updated. if (mPendingIdleUntil != null) { needRebatch = true; } } } if (!rebatching) { if (DEBUG_VALIDATE) { if (doValidate && !validateConsistencyLocked()) { Slog.v(TAG, "Tipping-point operation: type=" + a.type + " when=" + a.when + " when(hex)=" + Long.toHexString(a.when) + " whenElapsed=" + a.whenElapsed + " maxWhenElapsed=" + a.maxWhenElapsed + " interval=" + a.repeatInterval + " op=" + a.operation + " flags=0x" + Integer.toHexString(a.flags)); rebatchAllAlarmsLocked(false); needRebatch = false; } } if (needRebatch) { rebatchAllAlarmsLocked(false);//rebatch } rescheduleKernelAlarmsLocked(); updateNextAlarmClockLocked(); } }
我们先看fuzzForDuration函数:
static int fuzzForDuration(long duration) { if (duration < 15*60*1000) {// 小于15分钟 // If the duration until the time is less than 15 minutes, the maximum fuzz // is the duration. return (int)duration; } else if (duration < 90*60*1000) {//小于90分钟 // If duration is less than 1 1/2 hours, the maximum fuzz is 15 minutes, return 15*60*1000; } else { // Otherwise, we will fuzz by at most half an hour. return 30*60*1000;//最多半小时 } }
所以上面如果有该flag的alarm,设置下来,需要重新rebatch所有的alarm。
再来看下rebatchAllAlarmsLocked函数
void rebatchAllAlarmsLocked(boolean doValidate) { ArrayList<Batch> oldSet = (ArrayList<Batch>) mAlarmBatches.clone(); mAlarmBatches.clear(); Alarm oldPendingIdleUntil = mPendingIdleUntil; final long nowElapsed = SystemClock.elapsedRealtime(); final int oldBatches = oldSet.size(); for (int batchNum = 0; batchNum < oldBatches; batchNum++) {//遍历所有的batch Batch batch = oldSet.get(batchNum); final int N = batch.size(); for (int i = 0; i < N; i++) { reAddAlarmLocked(batch.get(i), nowElapsed, doValidate);// 遍历所有的batch中的alarm重新设置 } } if (oldPendingIdleUntil != null && oldPendingIdleUntil != mPendingIdleUntil) { Slog.wtf(TAG, "Rebatching: idle until changed from " + oldPendingIdleUntil + " to " + mPendingIdleUntil); if (mPendingIdleUntil == null) { // Somehow we lost this... we need to restore all of the pending alarms. restorePendingWhileIdleAlarmsLocked(); } } rescheduleKernelAlarmsLocked(); updateNextAlarmClockLocked(); }
reAddAlarmLocked函数,重新调用setImplLocked函数,注意其中的第二个参数是true
void reAddAlarmLocked(Alarm a, long nowElapsed, boolean doValidate) { a.when = a.origWhen; long whenElapsed = convertToElapsed(a.when, a.type); final long maxElapsed; if (a.windowLength == AlarmManager.WINDOW_EXACT) { // Exact maxElapsed = whenElapsed; } else { // Not exact. Preserve any explicit window, otherwise recalculate // the window based on the alarm's new futurity. Note that this // reflects a policy of preferring timely to deferred delivery. maxElapsed = (a.windowLength > 0) ? (whenElapsed + a.windowLength) : maxTriggerTime(nowElapsed, whenElapsed, a.repeatInterval); } a.whenElapsed = whenElapsed; a.maxWhenElapsed = maxElapsed; setImplLocked(a, true, doValidate);//注意第二个参数是true }
重新到setImplLocked函数:
else if (mPendingIdleUntil != null) {//现在该值不为空了 // We currently have an idle until alarm scheduled; if the new alarm has // not explicitly stated it wants to run while idle, then put it on hold. if ((a.flags&(AlarmManager.FLAG_ALLOW_WHILE_IDLE | AlarmManager.FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED | AlarmManager.FLAG_WAKE_FROM_IDLE))// 如果flag中没有这几个flag == 0) { mPendingWhileIdleAlarms.add(a);//加入pending列表 return;//返回了 } }
这样普通应用设置的alarm就无效了。
2.1 哪些设置可以在Idle状态下生效
那我们再看下都有哪些地方使用这些flag,可以在idle状态下,是alarm生效。
1. FLAG_WAKE_FROM_IDLE
FLAG_WAKE_FROM_IDLE 状态会在set的时候,如果是alarmClock会在flag上或上这个flag
if (alarmClock != null) { flags |= AlarmManager.FLAG_WAKE_FROM_IDLE | AlarmManager.FLAG_STANDALONE; }
2.FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED
FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED同样在set的时候,如果是系统进程,会或上这个flag
if (callingUid < Process.FIRST_APPLICATION_UID && workSource == null) { flags |= AlarmManager.FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED; }
3.FLAG_ALLOW_WHILE_IDLE
FLAG_ALLOW_WHILE_IDLE,这个flag在AlarmManager中提供两个接口,是允许应用在Idle状态下有设置alarm这个权限的。
public void setAndAllowWhileIdle(int type, long triggerAtMillis, PendingIntent operation) { setImpl(type, triggerAtMillis, WINDOW_HEURISTIC, 0, FLAG_ALLOW_WHILE_IDLE, operation, null, null); }
public void setExactAndAllowWhileIdle(int type, long triggerAtMillis, PendingIntent operation) { setImpl(type, triggerAtMillis, WINDOW_EXACT, 0, FLAG_ALLOW_WHILE_IDLE, operation, null, null); }
而当flag是FLAG_IDLE_UNTIL的alarm设置下来的时候,这个时候rebatching是false,不会去重新rebatch了。
if ((a.flags&AlarmManager.FLAG_IDLE_UNTIL) != 0) { mPendingIdleUntil = a; mConstants.updateAllowWhileIdleMinTimeLocked(); needRebatch = true; } else if ((a.flags&AlarmManager.FLAG_WAKE_FROM_IDLE) != 0) { if (mNextWakeFromIdle == null || mNextWakeFromIdle.whenElapsed > a.whenElapsed) { mNextWakeFromIdle = a; // If this wake from idle is earlier than whatever was previously scheduled, // and we are currently idling, then we need to rebatch alarms in case the idle // until time needs to be updated. if (mPendingIdleUntil != null) { needRebatch = true; } } } if (!rebatching) { if (DEBUG_VALIDATE) { if (doValidate && !validateConsistencyLocked()) { Slog.v(TAG, "Tipping-point operation: type=" + a.type + " when=" + a.when + " when(hex)=" + Long.toHexString(a.when) + " whenElapsed=" + a.whenElapsed + " maxWhenElapsed=" + a.maxWhenElapsed + " interval=" + a.repeatInterval + " op=" + a.operation + " flags=0x" + Integer.toHexString(a.flags)); rebatchAllAlarmsLocked(false); needRebatch = false; } } if (needRebatch) { rebatchAllAlarmsLocked(false); } rescheduleKernelAlarmsLocked(); updateNextAlarmClockLocked(); }
三、Idle状态结束处理
那何时才能正常使用alarm呢,肯定是flag是FLAG_IDLE_UNTIL的alarm发送完之后。
boolean triggerAlarmsLocked(ArrayList<Alarm> triggerList, final long nowELAPSED, final long nowRTC) { boolean hasWakeup = false; // batches are temporally sorted, so we need only pull from the // start of the list until we either empty it or hit a batch // that is not yet deliverable while (mAlarmBatches.size() > 0) { Batch batch = mAlarmBatches.get(0); if (batch.start > nowELAPSED) { // Everything else is scheduled for the future break; } // We will (re)schedule some alarms now; don't let that interfere // with delivery of this current batch mAlarmBatches.remove(0); final int N = batch.size(); for (int i = 0; i < N; i++) { Alarm alarm = batch.get(i); if ((alarm.flags&AlarmManager.FLAG_ALLOW_WHILE_IDLE) != 0) { // If this is an ALLOW_WHILE_IDLE alarm, we constrain how frequently the app can // schedule such alarms. long lastTime = mLastAllowWhileIdleDispatch.get(alarm.uid, 0); long minTime = lastTime + mAllowWhileIdleMinTime; if (nowELAPSED < minTime) { // Whoops, it hasn't been long enough since the last ALLOW_WHILE_IDLE // alarm went off for this app. Reschedule the alarm to be in the // correct time period. alarm.whenElapsed = minTime; if (alarm.maxWhenElapsed < minTime) { alarm.maxWhenElapsed = minTime; } setImplLocked(alarm, true, false); continue; } } alarm.count = 1; triggerList.add(alarm);//加入发送列表 if ((alarm.flags&AlarmManager.FLAG_WAKE_FROM_IDLE) != 0) { EventLogTags.writeDeviceIdleWakeFromIdle(mPendingIdleUntil != null ? 1 : 0, alarm.tag); } if (mPendingIdleUntil == alarm) {//如果发送的是mPendingIdleUntil mPendingIdleUntil = null;//置为null rebatchAllAlarmsLocked(false);//重置alarm restorePendingWhileIdleAlarmsLocked();//重新设置pending的alarm }我们再来看下rebatchAllAlarmsLocked函数
void rebatchAllAlarmsLocked(boolean doValidate) { ArrayList<Batch> oldSet = (ArrayList<Batch>) mAlarmBatches.clone(); mAlarmBatches.clear(); Alarm oldPendingIdleUntil = mPendingIdleUntil; final long nowElapsed = SystemClock.elapsedRealtime(); final int oldBatches = oldSet.size(); for (int batchNum = 0; batchNum < oldBatches; batchNum++) { Batch batch = oldSet.get(batchNum); final int N = batch.size(); for (int i = 0; i < N; i++) { reAddAlarmLocked(batch.get(i), nowElapsed, doValidate); } } if (oldPendingIdleUntil != null && oldPendingIdleUntil != mPendingIdleUntil) { Slog.wtf(TAG, "Rebatching: idle until changed from " + oldPendingIdleUntil + " to " + mPendingIdleUntil); if (mPendingIdleUntil == null) {//当mPendingIdleUntil为null // Somehow we lost this... we need to restore all of the pending alarms. restorePendingWhileIdleAlarmsLocked();//重新把pending的alarm设置下去 } } rescheduleKernelAlarmsLocked(); updateNextAlarmClockLocked(); }重新把pending的alarm设置下去
void restorePendingWhileIdleAlarmsLocked() { // Bring pending alarms back into the main list. if (mPendingWhileIdleAlarms.size() > 0) { ArrayList<Alarm> alarms = mPendingWhileIdleAlarms; mPendingWhileIdleAlarms = new ArrayList<>(); final long nowElapsed = SystemClock.elapsedRealtime(); for (int i=alarms.size() - 1; i >= 0; i--) { Alarm a = alarms.get(i); reAddAlarmLocked(a, nowElapsed, false); } } // Make sure we are using the correct ALLOW_WHILE_IDLE min time. mConstants.updateAllowWhileIdleMinTimeLocked(); // Reschedule everything. rescheduleKernelAlarmsLocked(); updateNextAlarmClockLocked(); // And send a TIME_TICK right now, since it is important to get the UI updated. try { mTimeTickSender.send(); } catch (PendingIntent.CanceledException e) { } }
这样我们能理解为什么当DeviceIdleController设置setIdleUntil接口后,其他应用设置的alarm会不生效。
- Android6.0 设备Idle状态(二)AlarmManagerService setIdleUntil接口
- Android6.0 设备Idle状态(一)DeviceIdleController
- Android6.0 设备Idle状态(三) PowerManagerService
- 深入android6.0 设备 idle状态
- Android6.0 旋转屏幕(二)旋转设备
- Android 6.0 设备Idle状态介绍
- Android8.0 AlarmManagerService
- Android6.0关于预置三方app卸载(二) 调用接口安装
- python的IDLE(二)
- Android6.0蓝牙搜索设备
- Android6.0 MTK 需求文档(二)
- android6.0 PowerManagerService状态分析
- Android6.0 按键流程(一)读取设备事件
- android6.0 otg连接设备 点亮屏幕(案例)
- Android6.0 按键流程(一)读取设备事件
- (二)使用IDLE进行编程
- 电脑Idle状态
- Android6.0-新控件(二)
- 在 Web 项目中应用 Apache Shiro
- Running (poj 3661)
- 使用Service和BroadcastReceiver实时监听网络状态
- CentOS7搭建Docker私有仓库
- 网站架构演化过程
- Android6.0 设备Idle状态(二)AlarmManagerService setIdleUntil接口
- catalan数
- 李白打酒问题
- android程序获取手机imei方法
- java跳出多重嵌套循环
- sublime实用扩展插件汇总
- 默认Spring DispatcherServlet 不支持 OPTIONS方法
- 如何在线程中获取spring 管理的bean
- NPOI2.2.0.0实例详解(十一)—向EXCEL插入图片