在第一篇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);
}
}