Android Doze模式

Android11 Doze模式相关类在

frameworks/base/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java

它是一个系统服务,一开机就运行

public class DeviceIdleController extends SystemService
        implements AnyMotionDetector.DeviceIdleCallback {

在启动阶段会初始化一些receiver,用于监听电池屏幕等变化,然后控制进入、退出Doze

    @Override
    public void onBootPhase(int phase) {
        if (phase == PHASE_SYSTEM_SERVICES_READY) {
            synchronized (this) {
               Intent(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED);
                mIdleIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY
                        | Intent.FLAG_RECEIVER_FOREGROUND);
                mLightIdleIntent = new Intent(PowerManager.ACTION_LIGHT_DEVICE_IDLE_MODE_CHANGED);
                mLightIdleIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY
                        | Intent.FLAG_RECEIVER_FOREGROUND);

                IntentFilter filter = new IntentFilter();
                filter.addAction(Intent.ACTION_BATTERY_CHANGED);
                getContext().registerReceiver(mReceiver, filter);

                filter = new IntentFilter();
                filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
                filter.addDataScheme("package");
                getContext().registerReceiver(mReceiver, filter);

                filter = new IntentFilter();
                filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
                getContext().registerReceiver(mReceiver, filter);

                filter = new IntentFilter();
                filter.addAction(Intent.ACTION_SCREEN_OFF);
                filter.addAction(Intent.ACTION_SCREEN_ON);
                getContext().registerReceiver(mInteractivityReceiver, filter);
        }
    }

1.進入Doze

要满足2个条件,一个是没有插电源,一个是屏幕关闭

首先断开电源,可以使用命令adb shell dumpsys battery unplug  这样方便debug。

断开电源后就会走receiver的battery change

                case Intent.ACTION_BATTERY_CHANGED: {
                    boolean present = intent.getBooleanExtra(BatteryManager.EXTRA_PRESENT, true);
                    boolean plugged = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, 0) != 0;
                    synchronized (DeviceIdleController.this) {
                        updateChargingLocked(present && plugged);
                    }
                } break;

然后走 updateChargingLocked

    void updateChargingLocked(boolean charging) {
        if (DEBUG) Slog.i(TAG, "updateChargingLocked: charging=" + charging);
        if (!charging && mCharging) {
            mCharging = false;
            if (!mForceIdle) {
                becomeInactiveIfAppropriateLocked();
            }
        } else if (charging) {
            mCharging = charging;
            if (!mForceIdle) {
                mActiveReason = ACTIVE_REASON_CHARGING;
                becomeActiveLocked("charging", Process.myUid());
            }
        }
    }

然后走becomeInactiveIfAppropriateLocked

   void becomeInactiveIfAppropriateLocked() {
        verifyAlarmStateLocked();

        final boolean isScreenBlockingInactive =
                mScreenOn && (!mConstants.WAIT_FOR_UNLOCK || !mScreenLocked);
        if (DEBUG) {
            Slog.d(TAG, "becomeInactiveIfAppropriateLocked():"
                    + " isScreenBlockingInactive=" + isScreenBlockingInactive
                    + " (mScreenOn=" + mScreenOn
                    + ", WAIT_FOR_UNLOCK=" + mConstants.WAIT_FOR_UNLOCK
                    + ", mScreenLocked=" + mScreenLocked + ")"
                    + " mCharging=" + mCharging
                    + " mForceIdle=" + mForceIdle
            );
        }
        Log.i("test","mDeepEnabled= "+mDeepEnabled
                    +" mQuickDozeActivated= "+mQuickDozeActivated
                    +" mState= "+mState
                    +" mLightState= "+mLightState);
        if (!mForceIdle && (mCharging || isScreenBlockingInactive)) {
            return;
        }
        // Become inactive and determine if we will ultimately go idle.
       
。。。。。。。
    }

因为屏幕是亮的,所以就直接return了。

        if (!mForceIdle && (mCharging || isScreenBlockingInactive)) {
            return;
        }

然后把屏幕关掉,就会走mInteractivityReceiver

    private final BroadcastReceiver mInteractivityReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            synchronized (DeviceIdleController.this) {
                updateInteractivityLocked();
            }
        }
    };

它是在启动准备阶段就监听了

                filter = new IntentFilter();
                filter.addAction(Intent.ACTION_SCREEN_OFF);
                filter.addAction(Intent.ACTION_SCREEN_ON);
                getContext().registerReceiver(mInteractivityReceiver, filter);

然后走updateInteractivityLocked

void updateInteractivityLocked() {
        // The interactivity state from the power manager tells us whether the display is
        // in a state that we need to keep things running so they will update at a normal
        // frequency.
        boolean screenOn = mPowerManager.isInteractive();
        if (DEBUG) Slog.d(TAG, "updateInteractivityLocked: screenOn=" + screenOn);
        if (!screenOn && mScreenOn) {
            mScreenOn = false;
            if (!mForceIdle) {
                becomeInactiveIfAppropriateLocked();
            }
        } else if (screenOn) {
            mScreenOn = true;
            if (!mForceIdle && (!mScreenLocked || !mConstants.WAIT_FOR_UNLOCK)) {
                mActiveReason = ACTIVE_REASON_SCREEN;
                becomeActiveLocked("screen", Process.myUid());
            }
        }
    }

然后又走到了 becomeInactiveIfAppropriateLocked

void becomeInactiveIfAppropriateLocked() {
        verifyAlarmStateLocked();

        final boolean isScreenBlockingInactive =
                mScreenOn && (!mConstants.WAIT_FOR_UNLOCK || !mScreenLocked);
        if (DEBUG) {
            Slog.d(TAG, "becomeInactiveIfAppropriateLocked():"
                    + " isScreenBlockingInactive=" + isScreenBlockingInactive
                    + " (mScreenOn=" + mScreenOn
                    + ", WAIT_FOR_UNLOCK=" + mConstants.WAIT_FOR_UNLOCK
                    + ", mScreenLocked=" + mScreenLocked + ")"
                    + " mCharging=" + mCharging
                    + " mForceIdle=" + mForceIdle
            );
        }
        Log.i("test","mDeepEnabled= "+mDeepEnabled
                    +" mQuickDozeActivated= "+mQuickDozeActivated
                    +" mState= "+mState
                    +" mLightState= "+mLightState);
        if (!mForceIdle && (mCharging || isScreenBlockingInactive)) {
            return;
        }
        // Become inactive and determine if we will ultimately go idle.
        if (mDeepEnabled) {
            if (mQuickDozeActivated) {
                if (mState == STATE_QUICK_DOZE_DELAY || mState == STATE_IDLE
                        || mState == STATE_IDLE_MAINTENANCE) {
                    // Already "idling". Don't want to restart the process.
                    // mLightState can't be LIGHT_STATE_ACTIVE if mState is any of these 3
                    // values, so returning here is safe.
                    return;
                }
                if (DEBUG) {
                    Slog.d(TAG, "Moved from "
                            + stateToString(mState) + " to STATE_QUICK_DOZE_DELAY");
                }
                mState = STATE_QUICK_DOZE_DELAY;
                // Make sure any motion sensing or locating is stopped.
                resetIdleManagementLocked();
                if (isUpcomingAlarmClock()) {
                    // If there's an upcoming AlarmClock alarm, we won't go into idle, so
                    // setting a wakeup alarm before the upcoming alarm is futile. Set the quick
                    // doze alarm to after the upcoming AlarmClock alarm.
                    Log.i("test","scheduleAlarmLocked 2924 "+ mConstants.QUICK_DOZE_DELAY_TIMEOUT);
                    scheduleAlarmLocked(
                            mAlarmManager.getNextWakeFromIdleTime() - mInjector.getElapsedRealtime()
                                    + mConstants.QUICK_DOZE_DELAY_TIMEOUT, false);
                } else {
                    Log.i("test","scheduleAlarmLocked 2930 "+ mConstants.QUICK_DOZE_DELAY_TIMEOUT);

                    // Wait a small amount of time in case something (eg: background service from
                    // recently closed app) needs to finish running.
                    scheduleAlarmLocked(mConstants.QUICK_DOZE_DELAY_TIMEOUT, false);
                }
                EventLogTags.writeDeviceIdle(mState, "no activity");
            } else if (mState == STATE_ACTIVE) {
                mState = STATE_INACTIVE;
                if (DEBUG) Slog.d(TAG, "Moved from STATE_ACTIVE to STATE_INACTIVE");
                resetIdleManagementLocked();
                long delay = mInactiveTimeout;
                if (shouldUseIdleTimeoutFactorLocked()) {
                    delay = (long) (mPreIdleFactor * delay);
                }
                if (isUpcomingAlarmClock()) {
                    Log.i("test","scheduleAlarmLocked 2946 "+ delay);

                    // If there's an upcoming AlarmClock alarm, we won't go into idle, so
                    // setting a wakeup alarm before the upcoming alarm is futile. Set the idle
                    // alarm to after the upcoming AlarmClock alarm.
                    scheduleAlarmLocked(
                            mAlarmManager.getNextWakeFromIdleTime() - mInjector.getElapsedRealtime()
                                    + delay, false);
                } else {
                    Log.i("test","scheduleAlarmLocked 2954 "+delay);

                    scheduleAlarmLocked(delay, 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();
            Log.i("test","scheduleLightAlarmLocked 2965 "+mConstants.LIGHT_IDLE_AFTER_INACTIVE_TIMEOUT);
            scheduleLightAlarmLocked(mConstants.LIGHT_IDLE_AFTER_INACTIVE_TIMEOUT);
            EventLogTags.writeDeviceIdleLight(mLightState, "no activity");
        }
    }

这次因为屏幕关闭了,所以会继续走下去,根据判断走到了Moved from STATE_ACTIVE to STATE_INACTIVE  设置一个scheduleAlarmLocked计时器。

void scheduleAlarmLocked(long delay, boolean idleUntil) {
        if (DEBUG) Slog.d(TAG, "scheduleAlarmLocked(" + delay + ", " + idleUntil + ")");

        if (mUseMotionSensor && mMotionSensor == null
                && mState != STATE_QUICK_DOZE_DELAY
                && mState != STATE_IDLE
                && mState != STATE_IDLE_MAINTENANCE) {
            // If there is no motion sensor on this device, but we need one, then we won't schedule
            // alarms, because we can't determine if the device is not moving.  This effectively
            // turns off normal execution of device idling, although it is still possible to
            // manually poke it by pretending like the alarm is going off.
            // STATE_QUICK_DOZE_DELAY skips the motion sensing so if the state is past the motion
            // sensing stage (ie, is QUICK_DOZE_DELAY, IDLE, or IDLE_MAINTENANCE), then idling
            // can continue until the user interacts with the device.
            return;
        }
        mNextAlarmTime = SystemClock.elapsedRealtime() + delay;
        if (idleUntil) {
            mAlarmManager.setIdleUntil(AlarmManager.ELAPSED_REALTIME_WAKEUP,
                    mNextAlarmTime, "DeviceIdleController.deep", mDeepAlarmListener, mHandler);
        } else {
            mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
                    mNextAlarmTime, "DeviceIdleController.deep", mDeepAlarmListener, mHandler);
        }
    }

当时间到了走stepIdleStateLocked

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

void stepIdleStateLocked(String reason) {
。。。。。。
}

然后再走Moved from LIGHT_STATE_ACTIVE to LIGHT_STATE_INACTIVE

设置一个计时器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);
    }

时间到了就走stepLightIdleStateLocked

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


void stepLightIdleStateLocked(String reason) {
。。。。。。
}

到这里代码就走完了,下面就等计时器触发然后再去改变Doze的状态,如果中间有唤醒屏幕或者充电,那么就退出Doze模式。

计时器触发了就会去设置新的状态,然后再开新的计时器这样循环。最后走到Moved from STATE_LOCATING to STATE_IDLE. ,等下一个计时器触发,再进入Moved from STATE_IDLE to STATE_IDLE_MAINTENANCE 处理一些事情,然后再回到Moved from STATE_IDLE_MAINTENANCE to STATE_IDLE,就这样在这2个状态循环切换,只是每次从IDLE进入MAINTENANCE的时间会越来越久。

你可以修改计时器的时间来快速进入Doze。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值