Android 8.1 Doze模式分析(三)Light Doze模式流程分析

在第一篇Android 8.1 Doze模式分析(一)我们知道,如果设备处于未充电且屏幕关闭状态,就会进入Light Doze模式,在LightDoze模式中,会定期进行维护,这种维护会持续N分钟,在维护状态(maintenance)时,会进行网络的访问,和同步、JobScheduler的操作,然后又会进入Idle状态,持续多次。之后如果设备仍旧保持静止,则会进入Deep Doze模式,因此,如果没有运动传感器,则无法检测设备是否处于静止,因此无法进入Deep Doze模式。

Light Doze模式相关值:

//表示轻度doze模式
private int mLightState;
//mLightState状态值,表示设备处于活动状态
private static final int LIGHT_STATE_ACTIVE = 0;
//mLightState状态值,表示设备处于不活动状态
private static final int LIGHT_STATE_INACTIVE = 1;
//mLightState状态值,表示设备进入空闲状态前,需要等待完成必要操作
private static final int LIGHT_STATE_PRE_IDLE = 3;
//mLightState状态值,表示设备处于空闲状态,该状态内将进行优化
private static final int LIGHT_STATE_IDLE = 4;
//mLightState状态值,表示设备处于空闲状态,要进入维护状态,先等待网络连接
private static final int LIGHT_STATE_WAITING_FOR_NETWORK = 5;
//mLightState状态值,表示设备处于维护状态
private static final int LIGHT_STATE_IDLE_MAINTENANCE = 6;
//mLightState状态值,表示设备处轻度Doze被覆盖,开始进入深度Doze模式
private static final int LIGHT_STATE_OVERRIDE = 7;

下面开始讲解模式流程

Android 8.1 Doze模式分析(一)讲到当屏幕灭屏或者充电结束时,就会调用becomeInactiveIfAppropriateLocked()

DeviceIdleController.java-->becomeInactiveIfAppropriateLocked()

    void becomeInactiveIfAppropriateLocked() {
        if (DEBUG) Slog.d(TAG, "becomeInactiveIfAppropriateLocked()");
        //仅当屏幕灭屏且没充电且mVoWifiCalling为faslse时,才能进入Doze模式,mForceIdle是shell设置相关
        if ((!mScreenOn && !mCharging && !mVoWifiCalling) || mForceIdle) {
            // Screen has turned off; we are now going to become inactive and start
            // waiting to see if we will ultimately go idle.
            if (mState == STATE_ACTIVE && mDeepEnabled) {
                mState = STATE_INACTIVE;
                if (DEBUG) Slog.d(TAG, "Moved from STATE_ACTIVE to STATE_INACTIVE");
                 //重置
                resetIdleManagementLocked();
                 //设置DeepAlarm,时间为30mins
                scheduleAlarmLocked(mInactiveTimeout, false);
                EventLogTags.writeDeviceIdle(mState, "no activity");
            }
            if (mLightState == LIGHT_STATE_ACTIVE && mLightEnabled) {
                mLightState = LIGHT_STATE_INACTIVE;
                if (DEBUG) Slog.d(TAG, "Moved from LIGHT_STATE_ACTIVE to LIGHT_STATE_INACTIVE");
                 //重置
                resetLightIdleManagementLocked();
                 //设置LightDoze,时间为5mins
                scheduleLightAlarmLocked(mConstants.LIGHT_IDLE_AFTER_INACTIVE_TIMEOUT);
                EventLogTags.writeDeviceIdleLight(mLightState, "no activity");
            }
        }
    }

当LightDoze处于LIGHT_STATE_ACTIVE状态,且mLightEnabled为true,就会将mLightState变为LIGHT_STATE_INACTIVE状态,表示不可交互状态(灭屏),调用resetLightIdleManagementLocked()重置,最后调用scheduleLightAlarmLocked()发送一个5分钟的alarm,

下面我们重点看scheduleLightAlarmLocked()

    void scheduleLightAlarmLocked(long delay) {
        if (DEBUG) Slog.d(TAG, "scheduleLightAlarmLocked(" + delay + ")");
        mNextLightAlarmTime = SystemClock.elapsedRealtime() + delay;
        mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
                mNextLightAlarmTime, "DeviceIdleController.light", mLightAlarmListener, mHandler);
    }

5分钟后到达mLightAlarmListener的回调方法onAlarm()

    private final AlarmManager.OnAlarmListener mLightAlarmListener
            = new AlarmManager.OnAlarmListener() {
        @Override
        public void onAlarm() {
            synchronized (DeviceIdleController.this) {
                stepLightIdleStateLocked("s:alarm");
            }
        }
    };

调用stepLightIdleStateLocked(),stepLightIdleStateLocked()方法负责LightDoze模式的改变

void stepLightIdleStateLocked(String reason) {
        //LIGHT_STATE_OVERRIDE表示已经进入深度睡眠了,直接退出
        if (mLightState == LIGHT_STATE_OVERRIDE) {
            // If we are already in deep device idle mode, then
            // there is nothing left to do for light mode.
            return;
        }

        if (DEBUG) Slog.d(TAG, "stepLightIdleStateLocked: mLightState=" + mLightState);
        EventLogTags.writeDeviceIdleLightStep();

        switch (mLightState) {
            case LIGHT_STATE_INACTIVE:
                mCurIdleBudget = mConstants.LIGHT_IDLE_MAINTENANCE_MIN_BUDGET;
                // Reset the upcoming idle delays.
                //mNextLightIdleDelay 表示LightDoze 进入空闲(Idle)状态的时间,5分钟
                mNextLightIdleDelay = mConstants.LIGHT_IDLE_TIMEOUT;
                //LightDoze进入维护状态(maintenance)的开始时间
                mMaintenanceStartTime = 0;
                //表示还有活动进行,给它们5分钟时间处理,如果没有活动进行的话,就直接进入到idle
                if (!isOpsInactiveLocked()) {
                    // We have some active ops going on...  give them a chance to finish
                    // before going in to our first idle.
                    mLightState = LIGHT_STATE_PRE_IDLE;
                    EventLogTags.writeDeviceIdleLight(mLightState, reason);
                    scheduleLightAlarmLocked(mConstants.LIGHT_PRE_IDLE_TIMEOUT);
                    break;
                }
                // Nothing active, fall through to immediately idle.
            case LIGHT_STATE_PRE_IDLE:
            case LIGHT_STATE_IDLE_MAINTENANCE:
                if (mMaintenanceStartTime != 0) {
                    long duration = SystemClock.elapsedRealtime() - mMaintenanceStartTime;
                    if (duration < mConstants.LIGHT_IDLE_MAINTENANCE_MIN_BUDGET) {
                        // We didn't use up all of our minimum budget; add this to the reserve.
                        mCurIdleBudget += (mConstants.LIGHT_IDLE_MAINTENANCE_MIN_BUDGET-duration);
                    } else {
                        // We used more than our minimum budget; this comes out of the reserve.
                        mCurIdleBudget -= (duration-mConstants.LIGHT_IDLE_MAINTENANCE_MIN_BUDGET);
                    }
                }
                mMaintenanceStartTime = 0;
                scheduleLightAlarmLocked(mNextLightIdleDelay);
                mNextLightIdleDelay = Math.min(mConstants.LIGHT_MAX_IDLE_TIMEOUT,
                        (long)(mNextLightIdleDelay * mConstants.LIGHT_IDLE_FACTOR));
                if (mNextLightIdleDelay < mConstants.LIGHT_IDLE_TIMEOUT) {
                    mNextLightIdleDelay = mConstants.LIGHT_IDLE_TIMEOUT;
                }
                if (DEBUG) Slog.d(TAG, "Moved to LIGHT_STATE_IDLE.");
                //LightDoze状态设置为LIGHT_STATE_IDLE
                mLightState = LIGHT_STATE_IDLE;
                EventLogTags.writeDeviceIdleLight(mLightState, reason);
                addEvent(EVENT_LIGHT_IDLE);
                //申请一个wakelock锁,保持CPU唤醒
                mGoingIdleWakeLock.acquire();
                //处理LightDoze进入Idle状态前的操作
                mHandler.sendEmptyMessage(MSG_REPORT_IDLE_ON_LIGHT);
                break;
            case LIGHT_STATE_IDLE:
            case LIGHT_STATE_WAITING_FOR_NETWORK:
                if (mNetworkConnected || mLightState == LIGHT_STATE_WAITING_FOR_NETWORK) {
                    // We have been idling long enough, now it is time to do some work.
                    mActiveIdleOpCount = 1;
                    mActiveIdleWakeLock.acquire();
                    mMaintenanceStartTime = SystemClock.elapsedRealtime();
                    if (mCurIdleBudget < mConstants.LIGHT_IDLE_MAINTENANCE_MIN_BUDGET) {
                        mCurIdleBudget = mConstants.LIGHT_IDLE_MAINTENANCE_MIN_BUDGET;
                    } else if (mCurIdleBudget > mConstants.LIGHT_IDLE_MAINTENANCE_MAX_BUDGET) {
                        mCurIdleBudget = mConstants.LIGHT_IDLE_MAINTENANCE_MAX_BUDGET;
                    }
                    scheduleLightAlarmLocked(mCurIdleBudget);
                    if (DEBUG) Slog.d(TAG,
                            "Moved from LIGHT_STATE_IDLE to LIGHT_STATE_IDLE_MAINTENANCE.");
                    mLightState = LIGHT_STATE_IDLE_MAINTENANCE;
                    EventLogTags.writeDeviceIdleLight(mLightState, reason);
                    addEvent(EVENT_LIGHT_MAINTENANCE);
                    mHandler.sendEmptyMessage(MSG_REPORT_IDLE_OFF);
                } else {
                    // We'd like to do maintenance, but currently don't have network
                    // connectivity...  let's try to wait until the network comes back.
                    // We'll only wait for another full idle period, however, and then give up.
                    scheduleLightAlarmLocked(mNextLightIdleDelay);
                    if (DEBUG) Slog.d(TAG, "Moved to LIGHT_WAITING_FOR_NETWORK.");
                    mLightState = LIGHT_STATE_WAITING_FOR_NETWORK;
                    EventLogTags.writeDeviceIdleLight(mLightState, reason);
                }
                break;
        }
    }

从这里可以看出从LIGHT_STATE_PRE_IDLE/LIGHT_STATE_IDLE_MAINTENANCE进入到LIGHT_STATE_IDLE/LIGHT_STATE_WAITING_FOR_NETWORK,或者从LIGHT_STATE_IDLE/LIGHT_STATE_WAITING_FOR_NETWORK进入到LIGHT_STATE_PRE_IDLE/LIGHT_STATE_IDLE_MAINTENANCE都会发送handler

final class MyHandler extends Handler {
        MyHandler(Looper looper) {
            super(looper);
        }

        @Override public void handleMessage(Message msg) {
            if (DEBUG) Slog.d(TAG, "handleMessage(" + msg.what + ")");
            switch (msg.what) {
                case MSG_WRITE_CONFIG: {
                    // Does not hold a wakelock. Just let this happen whenever.
                    handleWriteConfigFile();
                } break;
                case MSG_REPORT_IDLE_ON:
                case MSG_REPORT_IDLE_ON_LIGHT: {
                    // mGoingIdleWakeLock is held at this point
                    EventLogTags.writeDeviceIdleOnStart();
                    final boolean deepChanged;
                    final boolean lightChanged;
                    if (msg.what == MSG_REPORT_IDLE_ON) {
                        deepChanged = mLocalPowerManager.setDeviceIdleMode(true);
                        lightChanged = mLocalPowerManager.setLightDeviceIdleMode(false);
                    } else {
                        deepChanged = mLocalPowerManager.setDeviceIdleMode(false);
                        lightChanged = mLocalPowerManager.setLightDeviceIdleMode(true);
                    }
                    try {
                        mNetworkPolicyManager.setDeviceIdleMode(true);
                        mBatteryStats.noteDeviceIdleMode(msg.what == MSG_REPORT_IDLE_ON
                                ? BatteryStats.DEVICE_IDLE_MODE_DEEP
                                : BatteryStats.DEVICE_IDLE_MODE_LIGHT, null, Process.myUid());
                    } catch (RemoteException e) {
                    }
                    if (deepChanged) {
                        getContext().sendBroadcastAsUser(mIdleIntent, UserHandle.ALL);
                    }
                    if (lightChanged) {
                        getContext().sendBroadcastAsUser(mLightIdleIntent, UserHandle.ALL);
                    }
                    EventLogTags.writeDeviceIdleOnComplete();
                    mGoingIdleWakeLock.release();
                } break;
                case MSG_REPORT_IDLE_OFF: {
                    // mActiveIdleWakeLock is held at this point
                    EventLogTags.writeDeviceIdleOffStart("unknown");
                    final boolean deepChanged = mLocalPowerManager.setDeviceIdleMode(false);
                    final boolean lightChanged = mLocalPowerManager.setLightDeviceIdleMode(false);
                    try {
                        mNetworkPolicyManager.setDeviceIdleMode(false);
                        mBatteryStats.noteDeviceIdleMode(BatteryStats.DEVICE_IDLE_MODE_OFF,
                                null, Process.myUid());
                    } catch (RemoteException e) {
                    }
                    if (deepChanged) {
                        incActiveIdleOps();
                        getContext().sendOrderedBroadcastAsUser(mIdleIntent, UserHandle.ALL,
                                null, mIdleStartedDoneReceiver, null, 0, null, null);
                    }
                    if (lightChanged) {
                        incActiveIdleOps();
                        getContext().sendOrderedBroadcastAsUser(mLightIdleIntent, UserHandle.ALL,
                                null, mIdleStartedDoneReceiver, null, 0, null, null);
                    }
                    // Always start with one active op for the message being sent here.
                    // Now we are done!
                    decActiveIdleOps();
                    EventLogTags.writeDeviceIdleOffComplete();
                } break;
                case MSG_REPORT_ACTIVE: {
                    // The device is awake at this point, so no wakelock necessary.
                    String activeReason = (String)msg.obj;
                    int activeUid = msg.arg1;
                    EventLogTags.writeDeviceIdleOffStart(
                            activeReason != null ? activeReason : "unknown");
                    final boolean deepChanged = mLocalPowerManager.setDeviceIdleMode(false);
                    final boolean lightChanged = mLocalPowerManager.setLightDeviceIdleMode(false);
                    try {
                        mNetworkPolicyManager.setDeviceIdleMode(false);
                        mBatteryStats.noteDeviceIdleMode(BatteryStats.DEVICE_IDLE_MODE_OFF,
                                activeReason, activeUid);
                    } catch (RemoteException e) {
                    }
                    if (deepChanged) {
                        getContext().sendBroadcastAsUser(mIdleIntent, UserHandle.ALL);
                    }
                    if (lightChanged) {
                        getContext().sendBroadcastAsUser(mLightIdleIntent, UserHandle.ALL);
                    }
                    EventLogTags.writeDeviceIdleOffComplete();
                } break;
                case MSG_TEMP_APP_WHITELIST_TIMEOUT: {
                    // TODO: What is keeping the device awake at this point? Does it need to be?
                    int uid = msg.arg1;
                    checkTempAppWhitelistTimeout(uid);
                } break;
                case MSG_REPORT_MAINTENANCE_ACTIVITY: {
                    // TODO: What is keeping the device awake at this point? Does it need to be?
                    boolean active = (msg.arg1 == 1);
                    final int size = mMaintenanceActivityListeners.beginBroadcast();
                    try {
                        for (int i = 0; i < size; i++) {
                            try {
                                mMaintenanceActivityListeners.getBroadcastItem(i)
                                        .onMaintenanceActivityChanged(active);
                            } catch (RemoteException ignored) {
                            }
                        }
                    } finally {
                        mMaintenanceActivityListeners.finishBroadcast();
                    }
                } break;
                case MSG_FINISH_IDLE_OP: {
                    // mActiveIdleWakeLock is held at this point
                    decActiveIdleOps();
                } break;
            }
        }
    }

从这段代码可以看出在LightDoze进入IDLE状态或退出IDLE状态时时,首先通知PMS、NetworkPolicyManager设置DeviceIdleMode为true或false,然后发送一个Intent为mLightIdleIntent的广播

当LightDoze进入IDLE/MAINTENANCE状态时,在Handler中:

  • 1.通知NetworkPolicyManagerService限制/打开网络;
  • 2.发送广播,DeviceIdleJobsController中进行接受,限制/运行JobService.

如何限制网络?

DeviceIdleController中仅仅调用NetworkPolicyManagerService提供的接口,具体如何限制在NetworkPolicyManagerService内部实现:

mNetworkPolicyManager.setDeviceIdleMode(true)

后续流程流程请参看Android 8.1 Doze模式分析(二)对网络的限制

如何延迟Job?

private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
    @Override
    public void onReceive(Context context, Intent intent) {
        final String action = intent.getAction();
        //当DeepDoze或LightDoze的IDLE状态改变时,都会执行updateIdleMode()
        if (PowerManager.ACTION_LIGHT_DEVICE_IDLE_MODE
                _CHANGED.equals(action)|| PowerManager.ACTION_DEVICE
                _IDLE_MODE_CHANGED.equals(action)) {
            updateIdleMode(mPowerManager != null
                    ? (mPowerManager.isDeviceIdleMode()
                            || mPowerManager.isLightDeviceIdleMode())
                    : false);
        } else if (PowerManager.ACTION_POWER_SAVE_WHITELIST_
            CHANGED.equals(action)) {
            updateWhitelist();
        }
    }
};

DeviceIdleJobsController.java-->updateIdleMode()

void updateIdleMode(boolean enabled) {
    boolean changed = false;
    // Need the whitelist to be ready when going into idle
    if (mDeviceIdleWhitelistAppIds == null) {
        updateWhitelist();//更新白名单列表
    }
    synchronized (mLock) {
        if (mDeviceIdleMode != enabled) {
            changed = true;
        }
        mDeviceIdleMode = enabled;
        //遍历所有的Job
        mJobSchedulerService.getJobStore().forEachJob(mUpdateFunctor);
    }
    if (changed) {
        //回调到JobSchedulerService中,停止或开始Job
        mStateChangedListener.onDeviceIdleStateChanged(enabled);
    }
}

猜你喜欢

转载自blog.csdn.net/liu362732346/article/details/86580670
今日推荐