概述
Doze模式可以简单概括为:
若判断用户在连续的一段时间内没有使用手机,就延缓终端中APP后台的CPU和网络活动,以达到减少电量消耗的目的。
Doze模式(低电耗模式),是Andoriod6.0增加的一项系统服务,主要目的是为了优化电池性能,增加电池续航时间,Doze模式又分两种模式:深度Doze模式(Deep Doze)和轻度Doze模式(Light Doze),如果用户长时间没有主动使用其设备,处于静止状态且屏幕已关闭,则系统会使设备进入Doze模式,也就是深度Doze模式。如果用户关闭设备屏幕但仍处于移动状态时,则设备进入轻度Doze模式,此外,轻度Doze模式只适合Android7.0及以上版本。
当用户长时间未使用设备时,设备进入Doze模式,Doze模式会延迟应用后台 CPU 和网络活动,从而延长电池续航时间。处于Doze模式的设备会定期进入维护时段,在此期间,应用可以完成待进行的活动。然后,Doze模式会使设备重新进入较长时间的休眠状态,接着进入下一个维护时段。在达到几个小时的休眠时间上限之前,平台会周而复始地重复Doze模式休眠/维护的序列,且每一次都会延长Doze模式时长。处于Doze模式的设备始终可以感知到动作,且会在检测到动作时立即退出Doze模式。整个Doze图示如下:
上面这张图比较经典,基本上说明了Doze模式的含义。
图中的横轴表示时间,红色部分表示终端处于唤醒的运行状态,绿色部分就是Doze模式定义的休眠状态。
从图中的描述,我们可以看到:如果一个用户停止充电(on battery: 利用电池供电),关闭屏幕(screen off),手机处于静止状态(stationary: 位置没有发生相对移动),保持以上条件一段时间之后,终端就会进入Doze模式。一旦进入Doze模式,系统就减少(延缓)应用对网络的访问、以及对CPU的占用,来节省电池电量。
如图所示,Doze模式还定义了maintenance window。
在maintenance window中,系统允许应用完成它们被延缓的动作,即可以使用CPU资源及访问网络。
从图中我们可以看出,当进入Doze模式的条件一直满足时,Doze模式会定期的进入到maintenance window,但进入的间隔越来越长。
通过这种方式,Doze模式可以使终端处于较长时间的休眠状态。
需要注意的是:一旦Doze模式的条件不再满足,即用户充电、或打开屏幕、或终端的位置发生了移动,终端就恢复到正常模式。
因此,当用户频繁使用手机时,Doze模式几乎是没有什么实际用处的。
具体来讲,当终端处于Doze模式时,进行了以下操作:
1、暂停网络访问。
2、系统忽略所有的WakeLock。
3、标准的AlarmManager alarms被延缓到下一个maintenance window。
但使用AlarmManager的 setAndAllowWhileIdle、setExactAndAllowWhileIdle和setAlarmClock时,alarms定义事件仍会启动。
在这些alarms启动前,系统会短暂地退出Doze模式。
4、系统不再进行WiFi扫描。
5、系统不允许sync adapters运行。
6、系统不允许JobScheduler运行。
Deep Doze 和Light Doze模式对比如下:
DeviceIdleController的启动流程
开机时
SystemServer.java-->startOtherServices()
private void startOtherServices() {
.............
mSystemServiceManager.startService(DeviceIdleController.class);
............
}
构造方法:
public DeviceIdleController(Context context) {
super(context);
mConfigFile = new AtomicFile(new File(getSystemDir(), "deviceidle.xml"));
mHandler = new MyHandler(BackgroundThread.getHandler().getLooper());
// NOTE: Bug #627645 low power Feature BEG-->
mPowerControllerHelper = new PowerControllerHelper();
// <--NOTE: Bug #627645 low power Feature END
mAbsDeviceIdleController = (AbsDeviceIdleController)PMSFactory.getInstance().createExtraDeviceIdleController(context);
}
构造方法中,首先创建在/data/sytem下创建了deviceidle.xml文件,然后创建了一个Handler,并将BackgroundThread中Handler的Looper和该Handler进行绑定。BackgroundThread是一个单例模式实现的后台线程,可以用于任何一个进程。
DeviceIdleController.java-->onStart()
@Override
public void onStart() {
final PackageManager pm = getContext().getPackageManager();
synchronized (this) {
//Light doze模式和Deep Doze是否可用,默认不可用
mLightEnabled = mDeepEnabled = getContext().getResources().getBoolean(
com.android.internal.R.bool.config_enableAutoPowerModes);
//获取系统全局配置信息,如权限
SystemConfig sysConfig = SystemConfig.getInstance();
//从配置文件中读取省电模式下的白名单列表且不在在idle状态的白名单列表,即列表中的app能够在省电模式下在后台运行,
ArraySet<String> allowPowerExceptIdle = sysConfig.getAllowInPowerSaveExceptIdle();
for (int i=0; i<allowPowerExceptIdle.size(); i++) {
String pkg = allowPowerExceptIdle.valueAt(i);
try {
//获取白名单列表中的系统应用
ApplicationInfo ai = pm.getApplicationInfo(pkg,
PackageManager.MATCH_SYSTEM_ONLY);
int appid = UserHandle.getAppId(ai.uid);
//添加到Map中,表示这些应用在省电模式下可后台运行,但在Doze下后台不可运行
mPowerSaveWhitelistAppsExceptIdle.put(ai.packageName, appid);
//添加到SpareArray中,表示这是处于powersave白名单但不处于doze白名单的系统应用
mPowerSaveWhitelistSystemAppIdsExceptIdle.put(appid, true);
} catch (PackageManager.NameNotFoundException e) {
}
}
//从配置文件中读取时能够在省电模式白名单列表,也可以在DOZE模式下
ArraySet<String> allowPower = sysConfig.getAllowInPowerSave();
for (int i=0; i<allowPower.size(); i++) {
String pkg = allowPower.valueAt(i);
try {
ApplicationInfo ai = pm.getApplicationInfo(pkg,
PackageManager.MATCH_SYSTEM_ONLY);
int appid = UserHandle.getAppId(ai.uid);
mPowerSaveWhitelistAppsExceptIdle.put(ai.packageName, appid);
mPowerSaveWhitelistSystemAppIdsExceptIdle.put(appid, true);
//添加到DIC中的白名单列表中
mPowerSaveWhitelistApps.put(ai.packageName, appid);
//添加到DIC中的系统应用白名单列表中
mPowerSaveWhitelistSystemAppIds.put(appid, true);
} catch (PackageManager.NameNotFoundException e) {
}
}
//设置Contants内容观察者
mConstants = new Constants(mHandler, getContext().getContentResolver());
//读取/data/system/deviceidle.xml文件,将读取的app添加到表示用户设置的白名单中
readConfigFileLocked();
//更新白名单列表
updateWhitelistAppIdsLocked();
//网络是否连接
mNetworkConnected = true;
//屏幕是否保持常亮
mScreenOn = true;
// Start out assuming we are charging. If we aren't, we will at least get
// a battery update the next time the level drops.
//是否充电
mCharging = true;
mState = STATE_ACTIVE;//设备保持活动状态,深度Doze的初始值
mLightState = LIGHT_STATE_ACTIVE;//设备保持活动状态,轻度Doze的初始值
mInactiveTimeout = mConstants.INACTIVE_TIMEOUT;
}
mBinderService = new BinderService();
//发布远程服务
publishBinderService(Context.DEVICE_IDLE_CONTROLLER, mBinderService);
//发布本地服务
publishLocalService(LocalService.class, new LocalService());
}
在onStart()方法中,首先通过SystemConfig读取了两类白名单列表:在低电量模式下后台允许运行的应用的白名单、在低电量模式和Doze模式下都允许后台运行的应用白名单;
public ArraySet<String> getAllowInPowerSaveExceptIdle() {
return mAllowInPowerSaveExceptIdle;
}
public ArraySet<String> getAllowInPowerSave() {
return mAllowInPowerSave;
}
} else if ("allow-in-power-save-except-idle".equals(name) && allowAll) {
String pkgname = parser.getAttributeValue(null, "package");
if (pkgname == null) {
Slog.w(TAG, "<allow-in-power-save-except-idle> without package in "
+ permFile + " at " + parser.getPositionDescription());
} else {
mAllowInPowerSaveExceptIdle.add(pkgname);
}
XmlUtils.skipCurrentTag(parser);
continue;
} else if ("allow-in-power-save".equals(name) && allowAll) {
String pkgname = parser.getAttributeValue(null, "package");
if (pkgname == null) {
Slog.w(TAG, "<allow-in-power-save> without package in " + permFile + " at "
+ parser.getPositionDescription());
} else {
mAllowInPowerSave.add(pkgname);
}
XmlUtils.skipCurrentTag(parser);
continue;
我们再看下platform.xml文件
<!-- These are the standard packages that are white-listed to always have internet
access while in power save mode, even if they aren't in the foreground. -->
<allow-in-power-save package="com.android.providers.downloads" />
<allow-in-power-save package="com.sprd.ImsConnectionManager" />
<allow-in-power-save package="com.android.phone" />
<allow-in-power-save package="com.spreadtrum.vowifi" />
onStart()调用updateWhitelistAppIdsLocked()更新白名单列表
private void updateWhitelistAppIdsLocked() {
//处于省电模式白名单但不处于Idle状态白名单的app
mPowerSaveWhitelistExceptIdleAppIdArray =
buildAppIdArray(mPowerSaveWhitelistAppsExceptIdle,
mPowerSaveWhitelistUserApps, mPowerSaveWhitelistExceptIdleAppIds);
//处于所有的白名单的app,包括用户添加的
mPowerSaveWhitelistAllAppIdArray = buildAppIdArray(mPowerSaveWhitelistApps,
mPowerSaveWhitelistUserApps, mPowerSaveWhitelistAllAppIds);
//用户添加的白名单列表应用
mPowerSaveWhitelistUserAppIdArray = buildAppIdArray(null,
mPowerSaveWhitelistUserApps, mPowerSaveWhitelistUserAppIds);
if (mLocalActivityManager != null) {
//将适用于所有情况的白名单列表通知给AMS
mLocalActivityManager.setDeviceIdleWhitelist
(mPowerSaveWhitelistAllAppIdArray);
}
if (mLocalPowerManager != null) {
//将适用于所有情况的白名单列表通知给PMS
mLocalPowerManager.setDeviceIdleWhitelist(
mPowerSaveWhitelistAllAppIdArray);
}
if (mLocalAlarmManager != null) {
//将用户添加到白名单列表中的应用通知给AlarmManagerService
mLocalAlarmManager.setDeviceIdleUserWhitelist
(mPowerSaveWhitelistUserAppIdArray);
}
}
执行完onStart()方法后,就开始执行最后一个生命周期方法onBootPhase()
@Override
public void onBootPhase(int phase) {
if (phase == PHASE_SYSTEM_SERVICES_READY) {
synchronized (this) {
mAlarmManager = (AlarmManager) getContext().getSystemService
(Context.ALARM_SERVICE);
mBatteryStats = BatteryStatsService.getService();
mLocalActivityManager = getLocalService(ActivityManagerInternal.class);
mLocalPowerManager = getLocalService(PowerManagerInternal.class);
mPowerManager = getContext().getSystemService(PowerManager.class);
//申请一个wakelock锁保持CPU唤醒
mActiveIdleWakeLock = mPowerManager.newWakeLock
(PowerManager.PARTIAL_WAKE_LOCK,"deviceidle_maint");
//设置wakelock锁为非计数锁,只要执行一次release()就能释所有非计数锁
mActiveIdleWakeLock.setReferenceCounted(false);
mGoingIdleWakeLock = mPowerManager.newWakeLock
(PowerManager.PARTIAL_WAKE_LOCK,"deviceidle_going_idle");
//设置wakelock锁为计数锁,一次申请对应一次释放
mGoingIdleWakeLock.setReferenceCounted(true);
mConnectivityService = (ConnectivityService)ServiceManager.getService(
Context.CONNECTIVITY_SERVICE);
mLocalAlarmManager = getLocalService(AlarmManagerService.
LocalService.class);
mNetworkPolicyManager = INetworkPolicyManager.Stub.asInterface(
ServiceManager.getService
(Context.NETWORK_POLICY_SERVICE));
mSensorManager = (SensorManager)getContext().getSystemService
(Context.SENSOR_SERVICE);
//可用于自动省电模式时的传感器id,0表示没有可用传感器
int sigMotionSensorId = getContext().getResources().getInteger(
com.android.internal.R.integer.config_
autoPowerModeAnyMotionSensor);
if (sigMotionSensorId > 0) {
//根据传感器id获取传感器
mMotionSensor = mSensorManager.getDefaultSensor
(sigMotionSensorId, true);
}
//如果没有指定任一传感器&&在没有指定传感器情况下首选WristTilt
//传感器配置为true
if (mMotionSensor == null && getContext().getResources().getBoolean(
com.android.internal.R.bool.config_
autoPowerModePreferWristTilt)) {
//获取一个WristTilt传感器(手腕抖动)
mMotionSensor = mSensorManager.getDefaultSensor(
Sensor.TYPE_WRIST_TILT_GESTURE, true);
}
if (mMotionSensor == null) {
//如果以上条件都不满足,则获取一个SMD传感器
mMotionSensor = mSensorManager.getDefaultSensor(
Sensor.TYPE_SIGNIFICANT_MOTION, true);
}
//是否在进入Doze模式时预先获取位置
if (getContext().getResources().getBoolean(
com.android.internal.R.bool
.config_autoPowerModePrefetchLocation)) {
mLocationManager = (LocationManager) getContext().
getSystemService(Context.LOCATION_SERVICE);
mLocationRequest = new LocationRequest()
.setQuality(LocationRequest.ACCURACY_FINE)
.setInterval(0)
.setFastestInterval(0)
.setNumUpdates(1);
}
//自动省电模式下传感器检测的阈值度
float angleThreshold = getContext().getResources().getInteger(
com.android.internal.R.integer.config_
autoPowerModeThresholdAngle) / 100f;
//用于检测设备是否已静止
mAnyMotionDetector = new AnyMotionDetector(
(PowerManager) getContext().getSystemService
(Context.POWER_SERVICE),
mHandler, mSensorManager, this, angleThreshold);
//用于Doze状态发生改变时发送广播
mIdleIntent = new Intent(PowerManager.ACTION_DEVICE
_IDLE_MODE_CHANGED);
mIdleIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY
| Intent.FLAG_RECEIVER_FOREGROUND);
//用于当轻度Doze状态发生改变时发送广播
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);
//将适用于所有情况的白名单列表通知给AMS、PMS、AlarmManagerService
mLocalActivityManager.setDeviceIdleWhitelist(
mPowerSaveWhitelistAllAppIdArray);
mLocalPowerManager.setDeviceIdleWhitelist(
mPowerSaveWhitelistAllAppIdArray);
mLocalAlarmManager.setDeviceIdleUserWhitelist(
mPowerSaveWhitelistUserAppIdArray)
//更新交互状态
updateInteractivityLocked();
}
//更新网络连接状态
updateConnectivityState(null);
} else if (phase == PHASE_BOOT_COMPLETED) {
}
}
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) {//是否强制进入Idle
//进入Idle模式的入口方法
becomeInactiveIfAppropriateLocked();
}
} else if (screenOn) {
mScreenOn = true;
if (!mForceIdle) {
//退出Idle模式
becomeActiveLocked("screen", Process.myUid());
}
}
}
updateConnectivityState()用于更新网络状态
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) {//是否强制进入Idle
//进入Idle模式的入口方法
becomeInactiveIfAppropriateLocked();
}
} else if (screenOn) {
mScreenOn = true;
if (!mForceIdle) {
//退出Idle模式
becomeActiveLocked("screen", Process.myUid());
}
}
}
DeviceIdleController的状态变化
对于充电状态,在onBootPhase函数中已经提到,DeviceIdleController监听了ACTION_BATTERY_CHANGED广播:
............
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_BATTERY_CHANGED);
getContext().registerReceiver(mReceiver, filter);
...........
case Intent.ACTION_BATTERY_CHANGED: {
synchronized (DeviceIdleController.this) {
int plugged = intent.getIntExtra("plugged", 0);
updateChargingLocked(plugged != 0);
}
} break;
DeviceIdleController.java-->updateChargingLocked()
void updateChargingLocked(boolean charging) {
.........
if (!charging && mCharging) {
//从充电状态变为不充电状态
mCharging = false;
//mForceIdle值一般为false,是通过dumpsys命令将mForceIdle改成true的
if (!mForceIdle) {
//判断是否进入Doze模式
becomeInactiveIfAppropriateLocked();
}
} else if (charging) {
//进入充电状态
mCharging = charging;
if (!mForceIdle) {
//手机退出Doze模式
becomeActiveLocked("charging", Process.myUid());
}
}
}
我们先看下becomeInactiveIfAppropriateLocked()开始进入Doze模式
void becomeInactiveIfAppropriateLocked() {
.................
//屏幕熄灭,未充电
if ((!mScreenOn && !mCharging) || 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;
...............
//重置事件
resetIdleManagementLocked();
//开始检测是否可以进入Doze模式的Idle状态
//若终端没有watch feature, mInactiveTimeout时间为30min
scheduleAlarmLocked(mInactiveTimeout, false);
...............
}
if (mLightState == LIGHT_STATE_ACTIVE && mLightEnabled) {
mLightState = LIGHT_STATE_INACTIVE;
.............
resetLightIdleManagementLocked();//重置事件
scheduleLightAlarmLocked(mConstants.LIGHT_IDLE_AFTER_INACTIVE_TIMEOUT);
}
}
}
要进入Doze流程,就是调用这个函数,首先要保证屏幕灭屏然后没有充电。这里还有mDeepEnable和mLightEnable前面说过是在配置中定义的,一般默认是关闭(也就是不开Doze模式)。这里mLightEnabled是对应禁止wakelock持锁的,禁止网络。而mDeepEnabled对应是检测设备是否静止,除了禁止wakelock、禁止网络、还会机制alarm。
ight idle 和deep idle根据不同条件进入
void becomeInactiveIfAppropriateLocked() {
if (DEBUG) Slog.d(TAG, "becomeInactiveIfAppropriateLocked()");
if ((!mScreenOn && !mCharging) || 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();
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();
scheduleLightAlarmLocked(mConstants.LIGHT_IDLE_AFTER_INACTIVE_TIMEOUT);
EventLogTags.writeDeviceIdleLight(mLightState, "no activity");
}
}
light idle模式
light idle模式下会禁止网络、wakelock,但是不会禁止alarm
我们先看scheduleLightAlarmLocked函数,这里设置了一个alarm,delay是5分钟。到时间后调用mLightAlarmListener回调。
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);
}
mLightAlarmListener就是进入lightidle,调用stepLightIdleStateLocked函数
private final AlarmManager.OnAlarmListener mLightAlarmListener
= new AlarmManager.OnAlarmListener() {
@Override
public void onAlarm() {
synchronized (DeviceIdleController.this) {
stepLightIdleStateLocked("s:alarm");
}
}
};
我们来看stepLightIdleStateLocked函数,这个函数会处理mLightState不同状态,会根据不同状态,然后设置alarm,到时间后继续处理下个状态。到LIGHT_STATE_IDLE_MAINTENANCE状态处理时,会发送MSG_REPORT_IDLE_ON_LIGHT。这个消息的处理会禁止网络、禁止wakelock。然后到LIGHT_STATE_WAITING_FOR_NETWORK,会先退出Doze状态(这个时候网络、wakelock恢复)。然后设置alarm,alarm时间到后,还是在LIGHT_STATE_IDLE_MAINTENANCE状态。和之前一样(禁止网络、wakelock)。只是设置的alarm间隔会越来越大,也就是只要屏幕灭屏后,时间越长。设备会隔越来越长的时间才会退出Doze状态,这也符合一个实际情况,但是会有一个上限值。
void stepLightIdleStateLocked(String reason) {
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 = mConstants.LIGHT_IDLE_TIMEOUT;
mMaintenanceStartTime = 0;
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.");
mLightState = LIGHT_STATE_IDLE;
EventLogTags.writeDeviceIdleLight(mLightState, reason);
addEvent(EVENT_LIGHT_IDLE);
mGoingIdleWakeLock.acquire();
//发送消息,这个消息处理就会关闭网络,禁止wakelock
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);
//醒一下(开启网络、恢复wakelock)
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;
}
}
deep idle模式
下面我们再来看deep idle模式,这个模式除了禁止网络、wakelock还会禁止alarm。
我们再来看becomeInactiveIfAppropriateLocked函数中下面代码。是关于deep idle的设置 这里的mInactiveTimeout是半小时
void becomeInactiveIfAppropriateLocked() {
if (DEBUG) Slog.d(TAG, "becomeInactiveIfAppropriateLocked()");
if ((!mScreenOn && !mCharging) || 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();
scheduleAlarmLocked(mInactiveTimeout, false);
EventLogTags.writeDeviceIdle(mState, "no activity");
}
scheduleAlarmLocked(),注意如果这里参数idleUntil是true会调用AlarmManager的setIdleUntil函数,调用这个函数后普通应用设置alarm将失效。
void scheduleAlarmLocked(long delay, boolean idleUntil) {
if (mMotionSensor == null) {
//在onBootPhase时,获取过位置检测传感器
//如果终端没有配置位置检测传感器,那么终端永远不会进入到真正的Doze ilde状态
// If there is no motion sensor on this device, then we won't schedule
// alarms, because we can't determine if the device is not moving.
return;
}
mNextAlarmTime = SystemClock.elapsedRealtime() + delay;
if (idleUntil) {
//此时IdleUtil的值为false
mAlarmManager.setIdleUntil(AlarmManager.ELAPSED_REALTIME_WAKEUP,
mNextAlarmTime, "DeviceIdleController.deep", mDeepAlarmListener, mHandler);
} else {
//30min后唤醒,调用mDeepAlarmListener的onAlarm函数
mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
mNextAlarmTime, "DeviceIdleController.deep", mDeepAlarmListener, mHandler);
}
}
需要注意的是,DeviceIdleController一直在监控屏幕状态和充电状态,一但不满足Doze模式的条件,前面提到的becomeActiveLocked函数就会被调用。mAlarmManager设置的定时唤醒事件将被取消掉,mDeepAlarmListener的onAlarm函数不会被调用。
因此,我们知道了终端必须保持Doze模式的入口条件长达30min,才会进入mDeepAlarmListener.onAlarm:
private final AlarmManager.OnAlarmListener mDeepAlarmListener
= new AlarmManager.OnAlarmListener() {
@Override
public void onAlarm() {
synchronized (DeviceIdleController.this) {
//进入到stepIdleStateLocked函数
stepIdleStateLocked("s:alarm");
}
}
};
下面我们就来看下stepIdleStateLocked方法:
void stepIdleStateLocked(String reason) {
..........
final long now = SystemClock.elapsedRealtime();
//个人觉得,下面这段代码,是针对Idle状态设计的
//如果在Idle状态收到Alarm,那么将先唤醒终端,然后重新判断是否需要进入Idle态
//在介绍Doze模式原理时提到过,若应用调用AlarmManager的一些指定接口,仍然可以在Idle状态进行工作
if ((now+mConstants.MIN_TIME_TO_ALARM) > mAlarmManager.getNextWakeFromIdleTime()) {
// Whoops, there is an upcoming alarm. We don't actually want to go idle.
if (mState != STATE_ACTIVE) {
becomeActiveLocked("alarm", Process.myUid());
becomeInactiveIfAppropriateLocked();
}
return;
}
//以下是Doze模式的状态转变相关的代码
switch (mState) {
case STATE_INACTIVE:
// We have now been inactive long enough, it is time to start looking
// for motion and sleep some more while doing so.
//保持屏幕熄灭,同时未充电达到30min,进入此分支
//注册一个mMotionListener,检测是否移动
//如果检测到移动,将重新进入到ACTIVE状态
//相应代码比较直观,此处不再深入分析
startMonitoringMotionLocked();
//再次调用scheduleAlarmLocked函数,此次的时间仍为30min
//也就说如果不发生退出Doze模式的事件,30min后将再次进入到stepIdleStateLocked函数
//不过届时的mState已经变为STATE_IDLE_PENDING
scheduleAlarmLocked(mConstants.IDLE_AFTER_INACTIVE_TIMEOUT, false);
// Reset the upcoming idle delays.
//mNextIdlePendingDelay为5min
mNextIdlePendingDelay = mConstants.IDLE_PENDING_TIMEOUT;
//mNextIdleDelay为60min
mNextIdleDelay = mConstants.IDLE_TIMEOUT;
//状态变为STATE_IDLE_PENDING
mState = STATE_IDLE_PENDING;
............
break;
case STATE_IDLE_PENDING:
//保持息屏、未充电、静止状态,经过30min后,进入此分支
mState = STATE_SENSING;
//保持Doze模式条件,4min后再次进入stepIdleStateLocked
scheduleSensingTimeoutAlarmLocked(mConstants.SENSING_TIMEOUT);
//停止定位相关的工作
cancelLocatingLocked();
mNotMoving = false;
mLocated = false;
mLastGenericLocation = null;
mLastGpsLocation = null;
//开始检测手机是否发生运动(这里应该是更细致的侧重于角度的变化)
//若手机运动过,则重新变为active状态
mAnyMotionDetector.checkForAnyMotion();
break;
case STATE_SENSING:
//上面的条件满足后,进入此分支,开始获取定位信息
cancelSensingTimeoutAlarmLocked();
mState = STATE_LOCATING;
............
//保持条件30s,再次调用stepIdleStateLocked
scheduleAlarmLocked(mConstants.LOCATING_TIMEOUT, false);
//网络定位
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;
}
// If we have a location provider, we're all set, the listeners will move state
// forward.
if (mLocating) {
//无法定位则直接进入下一个case
break;
}
case STATE_LOCATING:
//停止定位和运动检测,直接进入到STATE_IDLE_MAINTENANCE
cancelAlarmLocked();
cancelLocatingLocked();
mAnyMotionDetector.stop();
case STATE_IDLE_MAINTENANCE:
//进入到这个case后,终端开始进入Idle状态,也就是真正的Doze模式
//定义退出Idle的时间此时为60min
scheduleAlarmLocked(mNextIdleDelay, true);
............
//退出周期逐步递增,每次乘2
mNextIdleDelay = (long)(mNextIdleDelay * mConstants.IDLE_FACTOR);
...........
//周期有最大值6h
mNextIdleDelay = Math.min(mNextIdleDelay, mConstants.MAX_IDLE_TIMEOUT);
if (mNextIdleDelay < mConstants.IDLE_TIMEOUT) {
mNextIdleDelay = mConstants.IDLE_TIMEOUT;
}
mState = STATE_IDLE;
...........
//通知PMS、NetworkPolicyManagerService等Doze模式开启,即进入Idle状态
//此时PMS disable一些非白名单WakeLock;NetworkPolicyManagerService开始限制一些应用的网络访问
//消息处理的具体流程比较直观,此处不再深入分析
mHandler.sendEmptyMessage(MSG_REPORT_IDLE_ON);
break;
case STATE_IDLE:
//进入到这个case时,本次的Idle状态暂时结束,开启maintenance window
// We have been idling long enough, now it is time to do some work.
mActiveIdleOpCount = 1;
mActiveIdleWakeLock.acquire();
//定义重新进入Idle的时间为5min (也就是手机可处于Maintenance window的时间)
scheduleAlarmLocked(mNextIdlePendingDelay, false);
mMaintenanceStartTime = SystemClock.elapsedRealtime();
//调整mNextIdlePendingDelay,乘2(最大为10min)
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;
...........
//通知PMS等暂时退出了Idle状态,可以进行一些工作
//此时PMS enable一些非白名单WakeLock;NetworkPolicyManagerService开始允许应用的网络访问
mHandler.sendEmptyMessage(MSG_REPORT_IDLE_OFF);
break;
}
}
上面的流程在注释里面已经很明白了,而我们在进入Deep idle时,发送了一个MSG_REPORT_IDLE_ON消息,我们看下面这个消息的处理和之前的MSG_REPORT_IDLE_ON_LIGHT一样的,关闭网络,禁止wakelock。
case MSG_REPORT_IDLE_ON:
case MSG_REPORT_IDLE_ON_LIGHT: {
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();
} break;
而禁止alarm是通过调用如下函数,注意参数是true。参数是true会调用mAlarmManager.setIdleUntil函数。这样其他的alarm会被滞后(除非在白名单中)
- scheduleAlarmLocked(mNextIdleDelay, true);
而每隔一段时间会进入Maintenance window的时间,此时是通过发送MSG_REPORT_IDLE_OFF消息,来恢复网络和wakelock。而这个时候之前设置的mAlarmManager.setIdleUntil的alarm也到期了,因此其他alarm也恢复了。但是这个时间只有5分钟,重新设置了alarm再次进入deep idle状态。