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会不生效。



1 0
原创粉丝点击