最初のAndroid8.1 Dozeモード分析(1)では、デバイスが充電されておらず、画面がオフの場合、Light Dozeモードに入ることがわかっています。LightDozeモードでは、定期的なメンテナンスが実行されます。このメンテナンスはメンテナンス状態(メンテナンス)では、ネットワークアクセス、同期、JobSchedulerの操作が実行され、アイドル状態になり、何度も継続します。その後、静止しているとディープドーズモードになりますので、モーションセンサーがないとデバイスが静止しているかどうかが検出できず、ディープドーズモードになりません。
ライトドーズモードの関連値:
//表示轻度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;
パターンプロセスの説明を始めましょう
でアンドロイド8.1 Dozeモード解析(1)画面がオフになっているか、または端の充電時の話、それは)(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-を送信します。分アラーム、
以下では、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_MAINに送信されることがわかります。
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に設定するように通知してから、mLightIdleIntentのインテントでブロードキャストを送信することがわかります。
LightDozeがIDLE / MAINTENANCE状態に入ると、ハンドラーで次のようになります。
- 1.NetworkPolicyManagerServiceにネットワークを制限/開くように通知します。
- 2.ブロードキャストを送信し、DeviceIdleJobsControllerで受け入れ、JobServiceを制限/実行します。
ネットワークを制限する方法は?
DeviceIdleControllerは、NetworkPolicyManagerServiceによって提供されるインターフェイスのみを呼び出します。NetworkPolicyManagerService内の実装を制限する方法:
mNetworkPolicyManager.setDeviceIdleMode(true)
フォローアッププロセスについては、Android 8.1 Dozeモード分析(2)ネットワークの制限を参照してください。
仕事を遅らせる方法は?
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);
}
}