Doze的退出,说的更严格一点,就是当Doze模式的状态由其他状态变为ACTIVE状态。简而言之,退出Doze模式有三种情况:屏幕亮屏、插入充电器、设备有移动。下面就这三种情况进行下分析。
在前面的分析中我们有见到过becomeActiveLocked()
方法,这个方法当时没有进行分析,这个方法就是用来退出Doze的,严格来说,是将Doze状态置为了ACTIVE状态,从而退出IDLE状态或MAINTENANCE状态。因此,不管是何种方式退出Doze,都会调用这个方法。该方法如下:
void becomeActiveLocked(String activeReason, int activeUid) {
if (mState != STATE_ACTIVE || mLightState != STATE_ACTIVE) {
//设置一个Handler,在Handler中通知PMS、发送idle changed 广播
scheduleReportActiveLocked(activeReason, activeUid);
//将DeepDoze状态值置为STATE_ACTIVE
mState = STATE_ACTIVE;
//将LightDoze状态值置为LIGHT_STATE_ACTIVE
mLightState = LIGHT_STATE_ACTIVE;
//多久后进入INACTIVE状态
mInactiveTimeout = mConstants.INACTIVE_TIMEOUT;
mCurIdleBudget = 0;
mMaintenanceStartTime = 0;
//重置DeepDoze相关属性值、alarm、listener等
resetIdleManagementLocked();
//重置LightDoze相关属性值、alarm、listener等
resetLightIdleManagementLocked();
}
}
当亮屏或灭屏时,PMS发送亮灭屏广播,DIC中监听了亮灭屏的广播,负责接受该广播并进行屏幕状态改变后的操作:
注册广播:
filter = new IntentFilter();
filter.addAction(Intent.ACTION_SCREEN_OFF);
filter.addAction(Intent.ACTION_SCREEN_ON);
getContext().registerReceiver(mInteractivityReceiver, filter);
接收到广播后进行处理:
private final BroadcastReceiver mInteractivityReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
synchronized (DeviceIdleController.this) {
updateInteractivityLocked();
}
}
};
在updateInteractivityLocked()
方法中更新LightDoze和DeepDoze的状态,这个方法在第一篇文章中分析过了。
当电池状态时,在BatteryService中会发送广播,DIC也对该广播进行了监听:
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_BATTERY_CHANGED);
getContext().registerReceiver(mReceiver, filter);
接收到广播后进行处理:
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override public void onReceive(Context context, Intent intent) {
switch (intent.getAction()) {
............
case Intent.ACTION_BATTERY_CHANGED: {
synchronized (DeviceIdleController.this) {
int plugged = intent.getIntExtra("plugged", 0);
updateChargingLocked(plugged != 0);
}
} break;
....................
}
}
};
当接收到该广播后,调用updateChargingLocked(plugged != 0)
来更新状态,参数表示是否插有充电线,该方法如下:
void updateChargingLocked(boolean charging) {
//由充电转变为放电
if (!charging && mCharging) {
mCharging = false;
if (!mForceIdle) {
//进入INACTIVE状态,开始Doze模式
becomeInactiveIfAppropriateLocked();
}
} else if (charging) {//由放电转变为充电
mCharging = charging;
if (!mForceIdle) {
//Doze状态退出ACTIVE状态,退出Doze模式
becomeActiveLocked("charging", Process.myUid());
}
}
}
最终,也会调用becomeActiveLocked()
退出Doze模式。
DeviceIdleController内部Binder也提供了用于退出Doze的接口:
@Override public void exitIdle(String reason) {
getContext().enforceCallingOrSelfPermission(Manifest.permission.DEVICE_POWER,
null);
long ident = Binder.clearCallingIdentity();
try {
exitIdleInternal(reason);
} finally {
Binder.restoreCallingIdentity(ident);
}
}
其内部也是通过调用becomeActiveLocked()方法退出Doze的:
public void exitIdleInternal(String reason) {
synchronized (this) {
becomeActiveLocked(reason, Binder.getCallingUid());
}
}
这种情况和以上几种情况略不同,这种情况下Doze的退出是瞬时的,会将两个状态值都转换为STATE_ACTIVE后,又会立即调用becomeInactiveIfAppropriateLocked()
方法进入INACTIVE状态,可以说是个“瞬时”退出。当MotionSensor检测到有移动时,则会退出Doze,处理逻辑在handleMotionDetectedLocked()
中:
void handleMotionDetectedLocked(long timeout, String type) {
boolean becomeInactive = false;
if (mState != STATE_ACTIVE) {
//通过Handler通知PMS、NetworkPolicy、发送IDLE_CHANGED广播等
scheduleReportActiveLocked(type, Process.myUid());
mState = STATE_ACTIVE;//将DeepDoze状态变为ACTIVE状态
mInactiveTimeout = timeout;
mCurIdleBudget = 0;
mMaintenanceStartTime = 0;
becomeInactive = true;
}
if (mLightState == LIGHT_STATE_OVERRIDE) {
mLightState = STATE_ACTIVE;//将LightDoze状态变为ACTIVE状态
becomeInactive = true;
}
if (becomeInactive) {
//进入INACTIVE状态
becomeInactiveIfAppropriateLocked();
}
}