Android 8.1 Doze mode analysis (three) Light Doze mode process analysis

In the first Android 8.1 Doze mode analysis (1), we know that if the device is not charged and the screen is off, it will enter the Light Doze mode. In the LightDoze mode, regular maintenance will be carried out. This maintenance will last for N minutes. In the maintenance state (maintenance), network access, synchronization, and JobScheduler operations will be performed, and then it will enter the Idle state, which will continue for many times. After that, if the device remains still, it will enter the Deep Doze mode. Therefore, if there is no motion sensor, it cannot detect whether the device is still, so it cannot enter the Deep Doze mode.

Related values ​​of Light Doze mode:

//表示轻度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;

Let's start to explain the pattern process

In Android 8.1 Doze mode analysis (1) talked about when the screen is off or charging is over, it will call 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");
            }
        }
    }

When LightDoze is in the LIGHT_STATE_ACTIVEstate and mLightEnabled is true, it will change mLightState to LIGHT_STATE_INACTIVE state, which means that the screen is not interactive (screen off), call resetLightIdleManagementLocked() to reset, and finally call scheduleLightAlarmLocked() to send a 5-minute alarm,

Below we focus on 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);
    }

Reach the callback method onAlarm() of mLightAlarmListener after 5 minutes

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

Call stepLightIdleStateLocked(), stepLightIdleStateLocked() method is responsible for the change of LightDoze mode

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;
        }
    }

It can be seen from here that the handler will be sent from LIGHT_STATE_PRE_IDLE/LIGHT_STATE_IDLE_MAINTENANCE to LIGHT_STATE_IDLE/LIGHT_STATE_WAITING_FOR_NETWORK, or from LIGHT_STATE_IDLE/LIGHT_STATE_WAITING_FOR_NETWORK to LIGHT_STATE_PRE_IDLE/LIGHT_STATE_IDLE_MAINTENANCE

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;
            }
        }
    }

It can be seen from this code that when LightDoze enters the IDLE state or exits the IDLE state, first notify the PMS, NetworkPolicyManager to set DeviceIdleMode to true or false, and then send a broadcast with an Intent of mLightIdleIntent

When LightDoze enters IDLE/MAINTENANCE state, in Handler:

  • 1. Notify NetworkPolicyManagerService to restrict/open the network;
  • 2. Send a broadcast, accept it in DeviceIdleJobsController, and restrict/run JobService.

How to restrict the network?

DeviceIdleController only calls the interface provided by NetworkPolicyManagerService. How to restrict the implementation within NetworkPolicyManagerService:

mNetworkPolicyManager.setDeviceIdleMode(true)

For the follow-up process, please refer to Android 8.1 Doze mode analysis (2) Restrictions on the network

How to delay the 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);
    }
}

 

Guess you like

Origin blog.csdn.net/liu362732346/article/details/86580670