Android Fk:[Doze] Android Doze Base on N

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初始化:
这里写图片描述
看到初始化里最重要的就是
1. 注册广播和监听器监听系统状态(充电状态和屏幕状态)
2. 从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限制

这里写图片描述

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状态:
1. 用户主动启动该App;
2. 该App当前有一个前台进程(或包含一个活动的前台服务,或被另一个activity或前台service使用);
3. App生成一个用户所能在锁屏或通知托盘看到的Notification, 而当用户设备插入电源时,系统将会释放App的待机状态,允许他们自由的连接网络及其执行未完成的工作和同步。如果设备空闲很长一段时间,系统将允许空闲App一天一次访问网络。

五、参考博客

Android M新特性Doze and App Standby模式详解

App Standby 学习小结

梦翼-的博客 Android N Idle模式分析

(原创)android6.0系统 Doze模式(DeviceIdle)实现与控制逻辑

猜你喜欢

转载自blog.csdn.net/TaylorPotter/article/details/79578532