public DeviceIdleController(Context context) {super(context);//deviceidle.xml用于定義idle模式也能正常工作的非系統應用mConfigFile = new AtomicFile(new File(getSystemDir(), "deviceidle.xml"));mHandler = new MyHandler(BackgroundThread.getHandler().getLooper());
}
public void onStart() {final PackageManager pm = getContext().getPackageManager();synchronized (this) {//讀取配置文件,推斷Doze模式是否同意被開啟mLightEnabled = mDeepEnabled = getContext().getResources().getBoolean(com.android.internal.R.bool.config_enableAutoPowerModes);//分析PKMS時提到過,PKMS掃描系統文件夾的xml,將形成SystemConfigSystemConfig sysConfig = SystemConfig.getInstance();//獲取除了device Idle模式外,都能夠執行的系統應用白名單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);mPowerSaveWhitelistAppsExceptIdle.put(ai.packageName, appid);mPowerSaveWhitelistSystemAppIdsExceptIdle.put(appid, true);} catch (PackageManager.NameNotFoundException e) {}}//獲取device Idle模式下,也能夠執行的系統應用白名單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);// These apps are on both the whitelist-except-idle as well// as the full whitelist, so they apply in all cases.mPowerSaveWhitelistAppsExceptIdle.put(ai.packageName, appid);mPowerSaveWhitelistSystemAppIdsExceptIdle.put(appid, true);mPowerSaveWhitelistApps.put(ai.packageName, appid);mPowerSaveWhitelistSystemAppIds.put(appid, true);} catch (PackageManager.NameNotFoundException e) {}}//Constants為deviceIdleController中的內部類,繼承ContentObserver//監控數據庫變化,同一時候得到Doze模式定義的一些時間間隔mConstants = new Constants(mHandler, getContext().getContentResolver());//解析deviceidle.xml,并將當中定義的package相應的app。增加到mPowerSaveWhitelistUserApps中readConfigFileLocked();//將白名單的內容給AlarmManagerService和PowerMangerService//比如:DeviceIdleController推斷開啟Doze模式時,會通知PMS//此時除去白名單相應的應用外,PMS會將其他全部的WakeLock設置為Disable狀態updateWhitelistAppIdsLocked();//下面的初始化,都是如果眼下處在進入Doze模式相反的條件上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;//Doze模式定義終端初始時為ACTIVE狀態mState = STATE_ACTIVE;//屏幕狀態初始時為ACTIVE狀態mLightState = LIGHT_STATE_ACTIVE;mInactiveTimeout = mConstants.INACTIVE_TIMEOUT;}//公布服務//BinderService和LocalService均為DeviceIdleController的內部類mBinderService = new BinderService();publishBinderService(Context.DEVICE_IDLE_CONTROLLER, mBinderService);publishLocalService(LocalService.class, new LocalService());
}
void updateDisplayLocked() {mCurDisplay = mDisplayManager.getDisplay(Display.DEFAULT_DISPLAY);// We consider any situation where the display is showing something to be it on,// because if there is anything shown we are going to be updating it at some// frequency so can't be allowed to go into deep sleeps.boolean screenOn = mCurDisplay.getState() == Display.STATE_ON;if (DEBUG) Slog.d(TAG, "updateDisplayLocked: screenOn=" + screenOn);if (!screenOn && mScreenOn) {mScreenOn = false;if (!mForceIdle) {//開始進入Doze模式becomeInactiveIfAppropriateLocked();}} else if (screenOn) {//屏幕點亮。退出Doze模式mScreenOn = true;if (!mForceIdle) {becomeActiveLocked("screen", Process.myUid());}}}
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時間為30minscheduleAlarmLocked(mInactiveTimeout, false);...............}if (mLightState == LIGHT_STATE_ACTIVE && mLightEnabled) {mLightState = LIGHT_STATE_INACTIVE;.............resetLightIdleManagementLocked();//重置事件scheduleLightAlarmLocked(mConstants.LIGHT_IDLE_AFTER_INACTIVE_TIMEOUT);}}
}
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;}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);//設置alarm,時間到后到下個步驟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);mHandler.sendEmptyMessage(MSG_REPORT_IDLE_ON_LIGHT);//發送消息。這個消息處理就會關閉網絡,禁止wakelockbreak;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);//醒一下(開啟網絡、恢復wakelock)} 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;}}
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");}
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的值為falsemAlarmManager.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);}
}
private final AlarmManager.OnAlarmListener mDeepAlarmListener= new AlarmManager.OnAlarmListener() {@Overridepublic 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_PENDINGscheduleAlarmLocked(mConstants.IDLE_AFTER_INACTIVE_TIMEOUT, false);// Reset the upcoming idle delays.//mNextIdlePendingDelay為5minmNextIdlePendingDelay = mConstants.IDLE_PENDING_TIMEOUT;//mNextIdleDelay為60minmNextIdleDelay = mConstants.IDLE_TIMEOUT;//狀態變為STATE_IDLE_PENDING mState = STATE_IDLE_PENDING;............break;case STATE_IDLE_PENDING://保持息屏、未充電、精巧狀態,經過30min后。進入此分支mState = STATE_SENSING;//保持Doze模式條件,4min后再次進入stepIdleStateLockedscheduleSensingTimeoutAlarmLocked(mConstants.SENSING_TIMEOUT);//停止定位相關的工作cancelLocatingLocked();mNotMoving = false;mLocated = false;mLastGenericLocation = null;mLastGpsLocation = null;//開始檢測手機是否發生運動(這里應該是更仔細的側重于角度的變化)//若手機運動過。則又一次變為active狀態mAnyMotionDetector.checkForAnyMotion();break;case STATE_SENSING://上面的條件滿足后。進入此分支。開始獲取定位信息cancelSensingTimeoutAlarmLocked();mState = STATE_LOCATING;............//保持條件30s,再次調用stepIdleStateLockedscheduleAlarmLocked(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) {//無法定位則直接進入下一個casebreak;}case STATE_LOCATING://停止定位和運動檢測,直接進入到STATE_IDLE_MAINTENANCEcancelAlarmLocked();cancelLocatingLocked();mAnyMotionDetector.stop();case STATE_IDLE_MAINTENANCE://進入到這個case后,終端開始進入Idle狀態,也就是真正的Doze模式//定義退出Idle的時間此時為60minscheduleAlarmLocked(mNextIdleDelay, true);............//退出周期逐步遞增,每次乘2mNextIdleDelay = (long)(mNextIdleDelay * mConstants.IDLE_FACTOR);...........//周期有最大值6hmNextIdleDelay = 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;}
}