概述
Deep Doze,也就是Android的Doze模式了,表示深度Doze,比起LightDoze,它将进行更多的限制:无法进行网络访问和 GPS/WLAN 扫描、唤醒被忽略、闹钟和作业/同步被延迟。当然,它的触发条件也将更加苛刻:灭屏、未充电、静止。
因此,如果要支持DeepDoze,则相应设备还必须具有大幅度动作检测器 (SMD),否则,无法探测到到底是静止还是移动。
原理
DIC中也定义了七个用来表示Deep Doze模式的值:
//表示doze模式
private int mState;
//mState值,表示设备处于活动状态
private static final int STATE_ACTIVE = 0;
//mState值,表示设备处于不交互状态,灭屏、静止
private static final int STATE_INACTIVE = 1;
//mState值,表示设备刚结束不交互状态,等待进入IDLE状态
private static final int STATE_IDLE_PENDING = 2;
//mState值,表示设备正在感应动作
private static final int STATE_SENSING = 3;
//mState值,表示设备正在定位
private static final int STATE_LOCATING = 4;
//mState值,表示设备处于空闲状态,也即Doze模式
private static final int STATE_IDLE = 5;
//mState值,表示设备正处于Doze模式,紧接着退出Doze进入维护状态
private static final int STATE_IDLE_MAINTENANCE = 6;
和Light Doze一样,DeepDoze模式也从becomeInactiveIfAppropriateLocked()
方法开始,这点已经在分析LightDoze时分析了,这里再来看一遍,该方法中涉及到Doze模式的代码如下:
void becomeInactiveIfAppropriateLocked() {if ((!mScreenOn && !mCharging) || mForceIdle) {if (mState == STATE_ACTIVE && mDeepEnabled) {//Doze模式状态由ACTIVE变为INACTIVEmState = STATE_INACTIVE;//重置Doze模式相关标记值、定时器resetIdleManagementLocked();//设置一个30mins的定时scheduleAlarmLocked(mInactiveTimeout, false);//30mins}.......................
}
当DeepDoze处于活动状态,并且Doze模式可用时,会通过Alarm一步步进入IDLE状态。首先会由交互状态变为不交互状态时,会重置所有数据后发送一个定时Alarm操作,重置操作如下:
void resetIdleManagementLocked() {mNextIdlePendingDelay = 0;mNextIdleDelay = 0;//下次LightDoze的定时时间mNextLightIdleDelay = 0;//取消DeepDoze发送的定时AlarmcancelAlarmLocked();//取消用于STATE_SENSING状态时长的AlarmcancelSensingTimeoutAlarmLocked();//取消GPS定位和Generic定位的更新cancelLocatingLocked();//取消、解绑接收传感器的触发事件stopMonitoringMotionLocked();//停止检测器检测mAnyMotionDetector.stop();
}
重置后会重新设置一个定时Alarm,时长为15分钟,代码如下:
void scheduleAlarmLocked(long delay, boolean idleUntil) {if (mMotionSensor == null) {//如果没有运动传感器,则返回,因为无法判断设备是否保持静止if (mMotionSensor == nullr) {return;}//设置DeepDoze的定时AlarmmNextAlarmTime = 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);}
}
当到达定时后,回调mDeepAlarmListener
的onAlarm()方法,该方法如下:
void scheduleAlarmLocked(long delay, boolean idleUntil) {if (mMotionSensor == null) {//如果没有运动传感器,则返回,因为无法判断设备是否保持静止if (mMotionSensor == nullr) {return;}//设置DeepDoze的定时AlarmmNextAlarmTime = 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);}
}
当到达定时后,回调mDeepAlarmListener的onAlarm()方法,该方法如下:
private final AlarmManager.OnAlarmListener mDeepAlarmListener= new AlarmManager.OnAlarmListener() {@Overridepublic void onAlarm() {synchronized (DeviceIdleController.this) {///每次Doze状态转换都会在该方法中进行stepIdleStateLocked("s:alarm");}}
};
stepIdleStateLocked()
方法中会通过Alarm在不同的阶段流转,该方法如下:
void stepIdleStateLocked(String reason) {final long now = SystemClock.elapsedRealtime();//说明1小时内有Alarm定时时间到,暂不进入IDLE状态,30min后再进入if ((now+mConstants.MIN_TIME_TO_ALARM) > mAlarmManager.getNextWakeFromIdleTime()) {if (mState != STATE_ACTIVE) {//将当前设备变为活动状态,LightDoze和DeepDoze都为Active状态becomeActiveLocked("alarm", Process.myUid());becomeInactiveIfAppropriateLocked();}return;}switch (mState) {case STATE_INACTIVE://启动SensorstartMonitoringMotionLocked();//设置STATE_IDLE_PENDING状态时长的定时Alarm,30minsscheduleAlarmLocked(mConstants.IDLE_AFTER_INACTIVE_TIMEOUT,false);// Reset the upcoming idle delays.mNextIdlePendingDelay = mConstants.IDLE_PENDING_TIMEOUT;//5minsmNextIdleDelay = mConstants.IDLE_TIMEOUT;//60mins//此时状态变为PENDING状态mState = STATE_IDLE_PENDING;break;case STATE_IDLE_PENDING://此时状态变为SENSING状态mState = STATE_SENSING;//设置STATE_SENSING状态超时时长的定时Alarm,DEBUG?1:4minsscheduleSensingTimeoutAlarmLocked(mConstants.SENSING_TIMEOUT);//取消通用位置更新和GPS位置更新cancelLocatingLocked();mNotMoving = false;mLocated = false;mLastGenericLocation = null;mLastGpsLocation = null;//开始检测是否有移动mAnyMotionDetector.checkForAnyMotion();break;case STATE_SENSING://取消用于STATE_SENSING状态超时时长的AlarmcancelSensingTimeoutAlarmLocked();//此时状态变为LOCATINGmState = STATE_LOCATING;//设置STATE_LOCATING状态时长的AlarmscheduleAlarmLocked(mConstants.LOCATING_TIMEOUT, false);//DEBUG?15:30//请求通用位置if (mLocationManager != null&& mLocationManager.getProvider(LocationManager.NETWORK_PROVIDER) != null) {mLocationManager.requestLocationUpdates(mLocationRequest,mGenericLocationListener, mHandler.getLooper());mLocating = true;} else {mHasNetworkLocation = false;}//请求GPS位置if (mLocationManager != null&& mLocationManager.getProvider(LocationManager.GPS_PROVIDER) != null) {mHasGps = true;mLocationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 1000, 5,mGpsLocationListener, mHandler.getLooper());mLocating = true;} else {mHasGps = false;}//如果true,则break,因为在Location的Listener中会进入下一个状态,//否则进入下一步状态if (mLocating) {break;}// Otherwise, we have to move from locating into idle maintenance.case STATE_LOCATING://取消DeepDoze的AlarmcancelAlarmLocked();//取消位置更新cancelLocatingLocked();//Sensor停止检测mAnyMotionDetector.stop();case STATE_IDLE_MAINTENANCE://设置STATE_IDLE状态时长的定时Alarm,到时后将退出IDLE状态scheduleAlarmLocked(mNextIdleDelay, true);//设置下次IDLE时间mNextIdleDelay = (long)(mNextIdleDelay * mConstants.IDLE_FACTOR);if (mNextIdleDelay < mConstants.IDLE_TIMEOUT) {mNextIdleDelay = mConstants.IDLE_TIMEOUT;}mState = STATE_IDLE;//进入DeepDoze的IDLE后,覆盖LightDozeif (mLightState != LIGHT_STATE_OVERRIDE) {mLightState = LIGHT_STATE_OVERRIDE;//取消LightDoze的定时AlarmcancelLightAlarmLocked();}//申请wakelock保持CPU唤醒mGoingIdleWakeLock.acquire();//handler中处理idle状态后各个模块的限制工作mHandler.sendEmptyMessage(MSG_REPORT_IDLE_ON);break;case STATE_IDLE:mActiveIdleOpCount = 1;//表示现在有正在活动的操作//申请wakelock锁保持cpu唤醒mActiveIdleWakeLock.acquire();//设置STATE_IDLE_MAINTENANCE状态时长的定时Alarm,//到时后将退出维护状态scheduleAlarmLocked(mNextIdlePendingDelay, false);mMaintenanceStartTime = SystemClock.elapsedRealtime();mNextIdlePendingDelay = Math.min(mConstants.MAX_IDLE_PENDING_TIMEOUT,(long)(mNextIdlePendingDelay * mConstants.IDLE_PENDING_FACTOR));if (mNextIdlePendingDelay < mConstants.IDLE_PENDING_TIMEOUT) {mNextIdlePendingDelay = mConstants.IDLE_PENDING_TIMEOUT;}mState = STATE_IDLE_MAINTENANCE;//Handler中处理退出idle状态进入维护状态后取消限制的工作mHandler.sendEmptyMessage(MSG_REPORT_IDLE_OFF);break;}
}
整个方法比较繁琐,具体作用都在代码中进行了注释,这样可能比较好理解点,这里再说明一点,由于设备是根据灭屏、设备静止来决定是否进入DeepDoze模式的,这其中有关于Sensor、AnyMotionDector相关逻辑,因此太细节的步骤就不分析了。
这里我们再根据上述方法分析,当DeepDoze进入IDLE状态时,通过Handler对网络、JobScheduler等进行限制的逻辑,Handler中DeepDoze进入IDLE状态和LightDoze进入IDLE状态的实现都在一个代码块中,在分析LightDoze时已经分析了大部分,这里删减只保留DeepDoze相关进行分析:
case MSG_REPORT_IDLE_ON:
case MSG_REPORT_IDLE_ON_LIGHT: {
.......................................deepChanged = mLocalPowerManager.setDeviceIdleMode(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);}
.......................................
} break;
DeepDoze进入IDLE状态时,Handler中做了三件事:
- 1.通知PMS、NetworkPolicyManager中DeepDoze处于IDLE状态。
- 2.BatteryStatsService中开始统计。
- 3.发送Intent为mIdleIntent的广播。
其中第一点就可以进行网络的限制。再来看看第三点,mIdIntent如下:
mIdleIntent = new Intent(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED);
mIdleIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY| Intent.FLAG_RECEIVER_FOREGROUND);
接受该广播的模块有如图所示这么多:
涉及模块有:JobScheduler,GNSS,UsageStatsService,Wifi,这些模块收到广播后,进行相应操作,从而对各自的功能进行限制。
再来看看当DeepDoze退出IDLE状态进入维护状态(matinenance)时的相关操作,和进入IDLE时的操作恰好相反,部分代码如下:
case MSG_REPORT_IDLE_OFF: {........................................final boolean deepChanged = mLocalPowerManager.setDeviceIdleMode(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);}........................................} break;
整个DeepDoze的原理就是这样。
1.如何忽略唤醒锁?
在进入DeepDoze Idle状态时有:
final boolean deepChanged = mLocalPowerManager.setDeviceIdleMode(true);
进入PowerManagerService中:
@Override
public boolean setDeviceIdleMode(boolean enabled) {return setDeviceIdleModeInternal(enabled);
}boolean setDeviceIdleModeInternal(boolean enabled) {synchronized (mLock) {if (mDeviceIdleMode == enabled) {return false;}mDeviceIdleMode = enabled;updateWakeLockDisabledStatesLocked();}return true;
}
再来看updateWakeLockDisabledStatesLocked()
方法:
private void updateWakeLockDisabledStatesLocked() {boolean changed = false;final int numWakeLocks = mWakeLocks.size();for (int i = 0; i < numWakeLocks; i++) {final WakeLock wakeLock = mWakeLocks.get(i);if ((wakeLock.mFlags & PowerManager.WAKE_LOCK_LEVEL_MASK)== PowerManager.PARTIAL_WAKE_LOCK) {//更新wakelock.disableif (setWakeLockDisabledStateLocked(wakeLock)) {changed = true;if (wakeLock.mDisabled) {// This wake lock is no longer being respected.notifyWakeLockReleasedLocked(wakeLock);} else {notifyWakeLockAcquiredLocked(wakeLock);}}}}if (changed) {mDirty |= DIRTY_WAKE_LOCKS;updatePowerStateLocked();}
}
最终通过设置wakelock.disable
完成wakelock的忽略。