#Android Fk:[Doze] Android Doze (base on N)
##一、概述
Base on Android N
当系统灭屏并长时间处于静止状态时,系统会进入Doze状态,此时
(1)限制网络访问
(2)限制wakelock(cpu锁)申请
(3)通过alarm manger设置的alarm 被推迟,除非设置了允许在idle状态也能工作的flag
(4)wifi scan被取消
(5)jobscheduler和sync被限制
##二、DeviceIdleController
###2.1 DeviceIdleController
DIC架构:
DIC初始化:
看到初始化里最重要的就是
- 注册广播和监听器监听系统状态(充电状态和屏幕状态)
- 从deviceidle.xml里读出白名单,然后将白名单设置到了对应的service中
###2.2 进入doze模式
####2.2.1 light Idle
看代码light idle在进入后仅仅限制了网络链接:
//frameworks/base/services/core/java/com/android/server/DeviceIdleController.java
void stepLightIdleStateLocked(String reason) {
switch (mLightState) {
// 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();
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;
}
}
####2.2.2 deep idle
在idle下会通过服务实例去设置idle模式从而进入idle:
void stepIdleStateLocked(String reason) {
switch (mState) {
case STATE_IDLE_MAINTENANCE:
scheduleAlarmLocked(mNextIdleDelay, true);
if (DEBUG) Slog.d(TAG, "Moved to STATE_IDLE. Next alarm in " + mNextIdleDelay +
" ms.");
mNextIdleDelay = (long)(mNextIdleDelay * mConstants.IDLE_FACTOR);
if (DEBUG) Slog.d(TAG, "Setting mNextIdleDelay = " + mNextIdleDelay);
mNextIdleDelay = Math.min(mNextIdleDelay, mConstants.MAX_IDLE_TIMEOUT);
if (mNextIdleDelay < mConstants.IDLE_TIMEOUT) {
mNextIdleDelay = mConstants.IDLE_TIMEOUT;
}
mState = STATE_IDLE;
if (mLightState != LIGHT_STATE_OVERRIDE) {
mLightState = LIGHT_STATE_OVERRIDE;
cancelLightAlarmLocked();
}
EventLogTags.writeDeviceIdle(mState, reason);
addEvent(EVENT_DEEP_IDLE);
mGoingIdleWakeLock.acquire();
mHandler.sendEmptyMessage(MSG_REPORT_IDLE_ON);
break;
case STATE_IDLE:
// We have been idling long enough, now it is time to do some work.
mActiveIdleOpCount = 1;
mActiveIdleWakeLock.acquire();
scheduleAlarmLocked(mNextIdlePendingDelay, false);
if (DEBUG) Slog.d(TAG, "Moved from STATE_IDLE to STATE_IDLE_MAINTENANCE. " +
"Next alarm in " + mNextIdlePendingDelay + " ms.");
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;
EventLogTags.writeDeviceIdle(mState, reason);
addEvent(EVENT_DEEP_MAINTENANCE);
mHandler.sendEmptyMessage(MSG_REPORT_IDLE_OFF);
break;
}
}
####2.2.3 DIC-状态变化因素
(1)screen 状态
(2)充电状态(charging)
(3)AnyMonitionDetetor 监测到的状态,其作用区间为进入STATE_SENSING开始到离开STATE_LOCATING。
(4)SignificantMotionSensor监测状态,其作用区间为进入STATE_IDLE_PENDING开始,到重新进入ACTIVE或者INACTIVE状态
(5)外部模块直接调用exitIdle
(6)使用dumpsys 的force-idle
(7)控制IDLE功能的总开关:config.xml:config_enableAutoPowerModes,默认为false
(8)当检测到线控耳机按钮事件(voice-search)时,会退出idle模式,实际还是调用DIC的exitIdle方法实现。
##三、Doze模式的影响
###3.1 DIC-Wakelock限制
###3.1 DIC-网络限制
###3.1 DIC-alarm限制
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PmrERzrS-1586831557401)(//img-blog.csdn.net/20180316104520665?watermark/2/text/Ly9ibG9nLmNzZG4ubmV0L1RheWxvclBvdHRlcg==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)]
###3.1 DIC-jobservice限制
##四、注意事项
###1.白名单设置方式:
###2.应用开发验证:
在Doze时测试APP
1 .系统版本大于等于6.0
2 . 连接USB,运行被测app,保持app在活动状态
3 . 关闭设备屏幕
4 . 通过下面的adb命令强制系统进入Doze模式
$ adb shell dumpsys battery unplug
$ adb shell dumpsys deviceidle step
5. 观察app表现行为是否有需优化改进的地方。
测试App Standby模式
1. 运行以下adb命令迫使应用程序进入App Standby模式:
$ adb shell dumpsys battery unplug
$ adb shell am set-inactive <packageName> true
2. 模拟唤醒你的应用程序使用以下命令:
$ adb shell am set-inactive <packageName> false
$ adb shell am get-inactive <packageName>
3. 观察你的App,确保应用程序恢复正常从待机模式过程中,App的通知及其背部活动能达到预期结果。
应用开发时需要验证doze模式下或者退出doze模式后行为是否正常:
###3.Doze和App Standby的区别
Doze模式需要屏幕关闭(通常晚上睡觉或长时间屏幕关闭才会进入),而App Standby不需要屏幕关闭,App进入后台一段时间也会受到连接网络等限制。
触发以下任意条件退出App Standby状态:
- 用户主动启动该App;
- 该App当前有一个前台进程(或包含一个活动的前台服务,或被另一个activity或前台service使用);
- App生成一个用户所能在锁屏或通知托盘看到的Notification, 而当用户设备插入电源时,系统将会释放App的待机状态,允许他们自由的连接网络及其执行未完成的工作和同步。如果设备空闲很长一段时间,系统将允许空闲App一天一次访问网络。
##五、参考博客