1. 程式人生 > >Android Fk:[Doze] Android Doze Base on N

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)實現與控制邏輯