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。