Android7.0 Doze模式分析(一)Doze介绍 & DeviceIdleController

参考:http://blog.csdn.net/gaugamela/article/details/52981984

Android M中,Google就引入了Doze模式。它定义了一种全新的、低能耗的状态。
 在该状态,后台只有部分任务被允许运行,其它任务都被强制停止。

在之前的博客中分析过Doze模式,就是device idle状态。可能有的地方分析的不是很详细,现在在android7.0上重新分析下。

一、基本原理

Doze模式可以简单概括为:
 若判断用户在连续的一段时间内没有使用手机,就延缓终端中APP后台的CPU和网络活动,以达到减少电量消耗的目的。


上面这张图比较经典,基本上说明了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运行。

另外我在另一篇博客中:http://blog.csdn.net/kc58236582/article/details/50554174也详细介绍了Doze模式,可以参考下,上面有一些命令使用等。


二、DeviceIdleController

Android中的Doze模式主要由DeviceIdleController来控制。

  1. public class DeviceIdleController extends SystemService  
  2.         implements AnyMotionDetector.DeviceIdleCallback   

可以看出DeviceIdleController继承自SystemService,是一个系统级的服务。
同时,继承了AnyMotionDetector定义的接口,便于检测到终端位置变化后进行回调。

2.1 DeviceIdleController的初始化

接下来我们看看它的初始化过程。

  1. private void startOtherServices() {  
  2.     .........  
  3.     mSystemServiceManager.startService(DeviceIdleController.class);  
  4.     .........  
  5. }  

如上代码所示,SystemServer在startOtherServices中启动了DeviceIdleController,将先后调用DeviceIdleController的构造函数和onStart函数。

构造函数
  1. public DeviceIdleController(Context context) {  
  2.     super(context);  
  3.     //deviceidle.xml用于定义idle模式也能正常工作的非系统应用  
  4.     mConfigFile = new AtomicFile(new File(getSystemDir(), "deviceidle.xml"));  
  5.     mHandler = new MyHandler(BackgroundThread.getHandler().getLooper());  
  6. }  

DeviceIdleController的构造函数比较简单,就是在创建data/system/deviceidle.xml对应的file文件,同时创建一个对应于后台线程的handler。这里的deviceidle.xml可以在设置中的电池选项那里。有电池优化,可以将一些应用放到白名单中,调用DeviceIdleController的addPowerSaveWhitelistApp方法,最后会写入deviceidle.xml文件,然后在下次开机的时候DeviceIdleController会重新读取deviceidle.xml文件然后放入白名单mPowerSaveWhitelistUserApps中。

onStart函数
  1. public void onStart() {  
  2.     final PackageManager pm = getContext().getPackageManager();  
  3.   
  4.     synchronized (this) {  
  5.         //读取配置文件,判断Doze模式是否允许被开启  
  6.         mLightEnabled = mDeepEnabled = getContext().getResources().getBoolean(  
  7.                 com.android.internal.R.bool.config_enableAutoPowerModes);  
  8.   
  9.         //分析PKMS时提到过,PKMS扫描系统目录的xml,将形成SystemConfig  
  10.         SystemConfig sysConfig = SystemConfig.getInstance();  
  11.   
  12.         //获取除了device Idle模式外,都可以运行的系统应用白名单  
  13.         ArraySet<String> allowPowerExceptIdle = sysConfig.getAllowInPowerSaveExceptIdle();  
  14.         for (int i=0; i<allowPowerExceptIdle.size(); i++) {  
  15.             String pkg = allowPowerExceptIdle.valueAt(i);  
  16.             try {  
  17.                 ApplicationInfo ai = pm.getApplicationInfo(pkg,  
  18.                         PackageManager.MATCH_SYSTEM_ONLY);  
  19.                 int appid = UserHandle.getAppId(ai.uid);  
  20.                 mPowerSaveWhitelistAppsExceptIdle.put(ai.packageName, appid);  
  21.                 mPowerSaveWhitelistSystemAppIdsExceptIdle.put(appid, true);  
  22.             } catch (PackageManager.NameNotFoundException e) {  
  23.             }  
  24.         }  
  25.   
  26.         //获取device Idle模式下,也可以运行的系统应用白名单  
  27.         ArraySet<String> allowPower = sysConfig.getAllowInPowerSave();  
  28.         for (int i=0; i<allowPower.size(); i++) {  
  29.              String pkg = allowPower.valueAt(i);  
  30.             try {  
  31.                 ApplicationInfo ai = pm.getApplicationInfo(pkg,  
  32.                          PackageManager.MATCH_SYSTEM_ONLY);  
  33.                 int appid = UserHandle.getAppId(ai.uid);  
  34.                 // These apps are on both the whitelist-except-idle as well  
  35.                 // as the full whitelist, so they apply in all cases.  
  36.                 mPowerSaveWhitelistAppsExceptIdle.put(ai.packageName, appid);  
  37.                 mPowerSaveWhitelistSystemAppIdsExceptIdle.put(appid, true);  
  38.                 mPowerSaveWhitelistApps.put(ai.packageName, appid);  
  39.                 mPowerSaveWhitelistSystemAppIds.put(appid, true);  
  40.             } catch (PackageManager.NameNotFoundException e) {  
  41.             }  
  42.         }  
  43.   
  44.         //Constants为deviceIdleController中的内部类,继承ContentObserver  
  45.         //监控数据库变化,同时得到Doze模式定义的一些时间间隔  
  46.         mConstants = new Constants(mHandler, getContext().getContentResolver());  
  47.   
  48.         //解析deviceidle.xml,并将其中定义的package对应的app,加入到mPowerSaveWhitelistUserApps中  
  49.         readConfigFileLocked();  
  50.   
  51.         //将白名单的内容给AlarmManagerService和PowerMangerService  
  52.         //例如:DeviceIdleController判断开启Doze模式时,会通知PMS  
  53.         //此时除去白名单对应的应用外,PMS会将其它所有的WakeLock设置为Disable状态  
  54.         updateWhitelistAppIdsLocked();  
  55.   
  56.         //以下的初始化,都是假设目前处在进入Doze模式相反的条件上  
  57.         mNetworkConnected = true;  
  58.         mScreenOn = true;  
  59.         // Start out assuming we are charging.  If we aren't, we will at least get  
  60.         // a battery update the next time the level drops.  
  61.         mCharging = true;  
  62.   
  63.         //Doze模式定义终端初始时为ACTIVE状态  
  64.         mState = STATE_ACTIVE;  
  65.         //屏幕状态初始时为ACTIVE状态  
  66.         mLightState = LIGHT_STATE_ACTIVE;  
  67.         mInactiveTimeout = mConstants.INACTIVE_TIMEOUT;  
  68.     }  
  69.   
  70.     //发布服务  
  71.     //BinderService和LocalService均为DeviceIdleController的内部类  
  72.     mBinderService = new BinderService();  
  73.     publishBinderService(Context.DEVICE_IDLE_CONTROLLER, mBinderService);  
  74.     publishLocalService(LocalService.classnew LocalService());  
  75. }  

除去发布服务外,DeviceIdleController在onStart函数中,主要是读取配置文件更新自己的变量,思路比较清晰。

在这里我们仅跟进一下updateWhitelistAppIdsLocked函数:

  1. private void updateWhitelistAppIdsLocked() {  
  2.     //构造出除去idle模式外,可运行的app id数组 (可认为是系统和普通应用的集合)  
  3.     //mPowerSaveWhitelistAppsExceptIdle从系统目录下的xml得到  
  4.     //mPowerSaveWhitelistUserApps从deviceidle.xml得到,或调用接口加入;  
  5.     //mPowerSaveWhitelistExceptIdleAppIds并未使用  
  6.     mPowerSaveWhitelistExceptIdleAppIdArray = buildAppIdArray(mPowerSaveWhitelistAppsExceptIdle,  
  7.             mPowerSaveWhitelistUserApps, mPowerSaveWhitelistExceptIdleAppIds);  
  8.   
  9.     //构造不受Doze限制的app id数组 (可认为是系统和普通应用的集合)  
  10.     //mPowerSaveWhitelistApps从系统目录下的xml得到  
  11.     //mPowerSaveWhitelistAllAppIds并未使用  
  12.     mPowerSaveWhitelistAllAppIdArray = buildAppIdArray(mPowerSaveWhitelistApps,  
  13.             mPowerSaveWhitelistUserApps, mPowerSaveWhitelistAllAppIds);  
  14.   
  15.     //构造不受Doze限制的app id数组(仅普通应用的集合)、  
  16.     //mPowerSaveWhitelistUserAppIds并未使用  
  17.     mPowerSaveWhitelistUserAppIdArray = buildAppIdArray(null,  
  18.             mPowerSaveWhitelistUserApps, mPowerSaveWhitelistUserAppIds);  
  19.   
  20.     if (mLocalPowerManager != null) {  
  21.         ...........  
  22.         //PMS拿到的是:系统和普通应用组成的不受Doze限制的app id数组   
  23.         mLocalPowerManager.setDeviceIdleWhitelist(mPowerSaveWhitelistAllAppIdArray);  
  24.     }  
  25.   
  26.     if (mLocalAlarmManager != null) {  
  27.         ..........  
  28.         //AlarmManagerService拿到的是:普通应用组成的不受Doze限制的app id数组   
  29.         mLocalAlarmManager.setDeviceIdleUserWhitelist(mPowerSaveWhitelistUserAppIdArray);  
  30.     }  
  31. }  

updateWhitelistAppIdsLocked主要是将白名单交给PMS和AlarmManagerService。
注意Android区分了系统应用白名单、普通应用白名单等,因此上面进行了一些合并操作。这里我们有没有发现,systemConfig的app不会加入alarm的白名单,而在Settings中电池那边设置的白名单,会加入Power wakelock的白名单。

onBootPhase函数

与PowerManagerService一样,DeviceIdleController在初始化的最后一个阶段需要调用onBootPhase函数:

  1. public void onBootPhase(int phase) {  
  2.     //在系统PHASE_SYSTEM_SERVICES_READY阶段,进一步完成一些初始化  
  3.     if (phase == PHASE_SYSTEM_SERVICES_READY) {  
  4.         synchronized (this) {  
  5.             //初始化一些变量  
  6.             mAlarmManager = (AlarmManager) getContext().getSystemService(Context.ALARM_SERVICE);  
  7.             ..............  
  8.   
  9.             mSensorManager = (SensorManager) getContext().getSystemService(Context.SENSOR_SERVICE);  
  10.             //根据配置文件,利用SensorManager获取对应的传感器,保存到mMotionSensor中  
  11.             ..............  
  12.   
  13.             //如果配置文件表明:终端需要预获取位置信息  
  14.             //则构造LocationRequest  
  15.             if (getContext().getResources().getBoolean(  
  16.                     com.android.internal.R.bool.config_autoPowerModePrefetchLocation)) {  
  17.                 mLocationManager = (LocationManager) getContext().getSystemService(  
  18.                         Context.LOCATION_SERVICE);  
  19.                 mLocationRequest = new LocationRequest()  
  20.                     .setQuality(LocationRequest.ACCURACY_FINE)  
  21.                     .setInterval(0)  
  22.                     .setFastestInterval(0)  
  23.                     .setNumUpdates(1);  
  24.             }  
  25.   
  26.             //根据配置文件,得到角度变化的门限  
  27.             float angleThreshold = getContext().getResources().getInteger(  
  28.                     com.android.internal.R.integer.config_autoPowerModeThresholdAngle) / 100f;  
  29.             //创建一个AnyMotionDetector,同时将DeviceIdleController注册到其中  
  30.             //当AnyMotionDetector检测到手机变化角度超过门限时,就会回调DeviceIdleController的接口  
  31.             mAnyMotionDetector = new AnyMotionDetector(  
  32.                     (PowerManager) getContext().getSystemService(Context.POWER_SERVICE),  
  33.                     mHandler, mSensorManager, this, angleThreshold);  
  34.   
  35.             //创建两个常用的Intent,用于通知Doze模式的变化  
  36.             mIdleIntent = new Intent(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED);  
  37.             mIdleIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY  
  38.                     | Intent.FLAG_RECEIVER_FOREGROUND);  
  39.             mLightIdleIntent = new Intent(PowerManager.ACTION_LIGHT_DEVICE_IDLE_MODE_CHANGED);  
  40.             mLightIdleIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY  
  41.                     | Intent.FLAG_RECEIVER_FOREGROUND);  
  42.   
  43.             //监听ACTION_BATTERY_CHANGED广播(电池信息发生改变)  
  44.             IntentFilter filter = new IntentFilter();  
  45.             filter.addAction(Intent.ACTION_BATTERY_CHANGED);  
  46.             getContext().registerReceiver(mReceiver, filter);  
  47.   
  48.             //监听ACTION_PACKAGE_REMOVED广播(包被移除)  
  49.             filter = new IntentFilter();  
  50.             filter.addAction(Intent.ACTION_PACKAGE_REMOVED);  
  51.             filter.addDataScheme("package");  
  52.             getContext().registerReceiver(mReceiver, filter);  
  53.   
  54.             //监听CONNECTIVITY_ACTION广播(连接状态发生改变)  
  55.             filter = new IntentFilter();  
  56.             filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);  
  57.             getContext().registerReceiver(mReceiver, filter);  
  58.   
  59.             //重新将白名单信息交给PowerManagerService和AlarmManagerService  
  60.             //这个工作在onStart函数中,已经调用updateWhitelistAppIdsLocked进行过了  
  61.             //到onBootPhase时,重新进行一次,可能:一是为了保险;二是,其它进程可能调用接口,更改了对应数据,于是进行更新  
  62.             mLocalPowerManager.setDeviceIdleWhitelist(mPowerSaveWhitelistAllAppIdArray);  
  63.             mLocalAlarmManager.setDeviceIdleUserWhitelist(mPowerSaveWhitelistUserAppIdArray);  
  64.   
  65.             //监听屏幕显示相关的变化  
  66.             mDisplayManager.registerDisplayListener(mDisplayListener, null);  
  67.   
  68.             //更新屏幕显示相关的信息  
  69.             updateDisplayLocked();  
  70.         }  
  71.         //更新连接状态相关的信息  
  72.         updateConnectivityState(null);  
  73.     }     
  74. }  

从代码可以看出,onBootPhase方法:
 主要创建一些本地变量,然后根据配置文件初始化一些传感器,同时注册了一些广播接收器和回到接口,
 最后更新屏幕显示和连接状态相关的信息。

2.2 DeviceIdleController的状态变化

充电状态的处理

对于充电状态,在onBootPhase函数中已经提到,DeviceIdleController监听了ACTION_BATTERY_CHANGED广播:

  1. ............  
  2. IntentFilter filter = new IntentFilter();  
  3. filter.addAction(Intent.ACTION_BATTERY_CHANGED);  
  4. getContext().registerReceiver(mReceiver, filter);  
  5. ...........  

我们看看receiver中对应的处理:

  1. private final BroadcastReceiver mReceiver = new BroadcastReceiver() {  
  2.     @Override public void onReceive(Context context, Intent intent) {  
  3.         switch (intent.getAction()) {  
  4.             .........  
  5.             case Intent.ACTION_BATTERY_CHANGED: {  
  6.                 synchronized (DeviceIdleController.this) {  
  7.                     //从广播中得到是否在充电的消息  
  8.                     int plugged = intent.getIntExtra("plugged", 0);  
  9.                     updateChargingLocked(plugged != 0);  
  10.                 }  
  11.             } break;  
  12.         }  
  13.     }  
  14. };  
根据上面的代码,可以看出当收到电池信息改变的广播后,DeviceIdleController将得到电源是否在充电的消息,然后调用updateChargingLocked函数进行处理。
  1. void updateChargingLocked(boolean charging) {  
  2.     .........  
  3.     if (!charging && mCharging) {  
  4.         //从充电状态变为不充电状态  
  5.         mCharging = false;  
  6.         //mForceIdle值一般为false,是通过dumpsys命令将mForceIdle改成true的  
  7.         if (!mForceIdle) {  
  8.             //判断是否进入Doze模式  
  9.             becomeInactiveIfAppropriateLocked();  
  10.         }  
  11.     } else if (charging) {  
  12.         //进入充电状态  
  13.         mCharging = charging;  
  14.         if (!mForceIdle) {  
  15.             //手机退出Doze模式  
  16.             becomeActiveLocked("charging", Process.myUid());  
  17.         }  
  18.     }  
  19. }  

becomeInactiveIfAppropriateLocked函数是开始进入Doze模式,而becomeActiveLocked是退出Doze模式。

显示状态处理

DeviceIdleController中注册了显示变化的回调

  1. mDisplayManager.registerDisplayListener(mDisplayListener, null);  
回调会调用updateDisplayLocked函数
  1. private final DisplayManager.DisplayListener mDisplayListener  
  2.         = new DisplayManager.DisplayListener() {  
  3.     @Override public void onDisplayAdded(int displayId) {  
  4.     }  
  5.   
  6.     @Override public void onDisplayRemoved(int displayId) {  
  7.     }  
  8.   
  9.     @Override public void onDisplayChanged(int displayId) {  
  10.         if (displayId == Display.DEFAULT_DISPLAY) {  
  11.             synchronized (DeviceIdleController.this) {  
  12.                 updateDisplayLocked();  
  13.             }  
  14.         }  
  15.     }  
  16. };  

updateDisplayLocked函数和更新充电状态的函数updateChargingLocked类似

  1. void updateDisplayLocked() {  
  2.     mCurDisplay = mDisplayManager.getDisplay(Display.DEFAULT_DISPLAY);  
  3.     // We consider any situation where the display is showing something to be it on,  
  4.     // because if there is anything shown we are going to be updating it at some  
  5.     // frequency so can't be allowed to go into deep sleeps.  
  6.     boolean screenOn = mCurDisplay.getState() == Display.STATE_ON;  
  7.     if (DEBUG) Slog.d(TAG, "updateDisplayLocked: screenOn=" + screenOn);  
  8.     if (!screenOn && mScreenOn) {  
  9.         mScreenOn = false;  
  10.         if (!mForceIdle) {//开始进入Doze模式  
  11.             becomeInactiveIfAppropriateLocked();  
  12.         }  
  13.     } else if (screenOn) {//屏幕点亮,退出Doze模式  
  14.         mScreenOn = true;  
  15.         if (!mForceIdle) {  
  16.             becomeActiveLocked("screen", Process.myUid());  
  17.         }  
  18.     }  
  19. }  
becomeActiveLocked函数退出Doze模式

我们先来看看becomeActiveLocked函数

  1. //activeReason记录的终端变为active的原因  
  2. void becomeActiveLocked(String activeReason, int activeUid) {  
  3.     ...........  
  4.     if (mState != STATE_ACTIVE || mLightState != STATE_ACTIVE) {  
  5.         ............  
  6.         //1、通知PMS等Doze模式结束  
  7.         scheduleReportActiveLocked(activeReason, activeUid);  
  8.   
  9.         //更新DeviceIdleController本地维护的状态  
  10.         //在DeviceIdleController的onStart函数中,我们已经知道了  
  11.         //初始时,mState和mLightState均为Active状态  
  12.         mState = STATE_ACTIVE;//state是指设备通过传感器判断进入idle  
  13.         mLightState = LIGHT_STATE_ACTIVE;//mLight是背光的状态  
  14.   
  15.         mInactiveTimeout = mConstants.INACTIVE_TIMEOUT;  
  16.         mCurIdleBudget = 0;  
  17.         mMaintenanceStartTime = 0;  
  18.   
  19.         //2、重置一些事件  
  20.         resetIdleManagementLocked();  
  21.         resetLightIdleManagementLocked();  
  22.   
  23.         addEvent(EVENT_NORMAL);  
  24.     }  
  25. }  

scheduleReportActiveLocked函数就是发送MSG_REPORT_ACTIVE消息

  1. void scheduleReportActiveLocked(String activeReason, int activeUid) {  
  2.     //发送MSG_REPORT_ACTIVE消息  
  3.     Message msg = mHandler.obtainMessage(MSG_REPORT_ACTIVE, activeUid, 0, activeReason);  
  4.     mHandler.sendMessage(msg);  
  5. }  

我们再看下消息的处理,主要调用了PowerManagerService的setDeviceIdleMode函数来退出Doze状态,然后重新更新wakelock的enable状态, 以及通知NetworkPolicyManagerService不再限制应用上网,还有发送Doze模式改变的广播。

  1. .........  
  2. case MSG_REPORT_ACTIVE: {  
  3.     .........  
  4.     //通知PMS Doze模式结束,  
  5.     //于是PMS将一些Doze模式下,disable的WakeLock重新enable  
  6.     //然后调用updatePowerStateLocked函数更新终端的状态  
  7.     final boolean deepChanged = mLocalPowerManager.setDeviceIdleMode(false);  
  8.     final boolean lightChanged = mLocalPowerManager.setLightDeviceIdleMode(false);  
  9.   
  10.     try {  
  11.         //通过NetworkPolicyManagerService更改Ip-Rule,不再限制终端应用上网  
  12.         mNetworkPolicyManager.setDeviceIdleMode(false);  
  13.         //BSS做好对应的记录  
  14.         mBatteryStats.noteDeviceIdleMode(BatteryStats.DEVICE_IDLE_MODE_OFF,  
  15.                 activeReason, activeUid);  
  16.     } catch (RemoteException e) {  
  17.     }  
  18.   
  19.     //发送广播  
  20.     if (deepChanged) {  
  21.         getContext().sendBroadcastAsUser(mIdleIntent, UserHandle.ALL);  
  22.     }  
  23.     if (lightChanged) {  
  24.         getContext().sendBroadcastAsUser(mLightIdleIntent, UserHandle.ALL);  
  25.     }  
  26. }  
  27. ........  

resetIdleManagementLocked函数就是取消alarm,检测等。

  1. void resetIdleManagementLocked() {  
  2.     //复位一些状态变量  
  3.     mNextIdlePendingDelay = 0;  
  4.     mNextIdleDelay = 0;  
  5.     mNextLightIdleDelay = 0;  
  6.   
  7.     //停止一些工作,主要是位置检测相关的  
  8.     cancelAlarmLocked();  
  9.     cancelSensingTimeoutAlarmLocked();  
  10.     cancelLocatingLocked();  
  11.     stopMonitoringMotionLocked();  
  12.     mAnyMotionDetector.stop();  
  13. }  

becomeInactiveIfAppropriateLocked函数开始进入Doze模式

becomeInactiveIfAppropriateLocked函数就是我们开始进入Doze模式的第一个步骤,下面我们就详细分析这个函数

  1. void becomeInactiveIfAppropriateLocked() {  
  2.     .................  
  3.     //屏幕熄灭,未充电  
  4.     if ((!mScreenOn && !mCharging) || mForceIdle) {  
  5.         // Screen has turned off; we are now going to become inactive and start  
  6.         // waiting to see if we will ultimately go idle.  
  7.         if (mState == STATE_ACTIVE && mDeepEnabled) {  
  8.             mState = STATE_INACTIVE;  
  9.             ...............  
  10.             //重置事件  
  11.             resetIdleManagementLocked();  
  12.   
  13.             //开始检测是否可以进入Doze模式的Idle状态  
  14.             //若终端没有watch feature, mInactiveTimeout时间为30min  
  15.             scheduleAlarmLocked(mInactiveTimeout, false);  
  16.             ...............  
  17.         }  
  18.   
  19.         if (mLightState == LIGHT_STATE_ACTIVE && mLightEnabled) {  
  20.             mLightState = LIGHT_STATE_INACTIVE;  
  21.             .............  
  22.             resetLightIdleManagementLocked();//重置事件  
  23.             scheduleLightAlarmLocked(mConstants.LIGHT_IDLE_AFTER_INACTIVE_TIMEOUT);  
  24.         }  
  25.     }  
  26. }  

要进入Doze流程,就是调用这个函数,首先要保证屏幕灭屏然后没有充电。这里还有mDeepEnable和mLightEnable前面说过是在配置中定义的,一般默认是关闭(也就是不开Doze模式)。这里mLightEnabled是对应禁止wakelock持锁的,禁止网络。而mDeepEnabled对应是检测设备是否静止,除了禁止wakelock、禁止网络、还会机制alarm。


light 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回调。

  1. void scheduleLightAlarmLocked(long delay) {  
  2.     if (DEBUG) Slog.d(TAG, "scheduleLightAlarmLocked(" + delay + ")");  
  3.     mNextLightAlarmTime = SystemClock.elapsedRealtime() + delay;  
  4.     mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,  
  5.             mNextLightAlarmTime, "DeviceIdleController.light", mLightAlarmListener, mHandler);  
  6. }  

mLightAlarmListener就是进入lightidle,调用stepLightIdleStateLocked函数

  1. private final AlarmManager.OnAlarmListener mLightAlarmListener  
  2.         = new AlarmManager.OnAlarmListener() {  
  3.     @Override  
  4.     public void onAlarm() {  
  5.         synchronized (DeviceIdleController.this) {  
  6.             stepLightIdleStateLocked("s:alarm");  
  7.         }  
  8.     }  
  9. };  

我们来看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状态,这也符合一个实际情况,但是会有一个上限值。

  1. void stepLightIdleStateLocked(String reason) {  
  2.     if (mLightState == LIGHT_STATE_OVERRIDE) {  
  3.         // If we are already in deep device idle mode, then  
  4.         // there is nothing left to do for light mode.  
  5.         return;  
  6.     }  
  7.   
  8.     EventLogTags.writeDeviceIdleLightStep();  
  9.   
  10.     switch (mLightState) {  
  11.         case LIGHT_STATE_INACTIVE:  
  12.             mCurIdleBudget = mConstants.LIGHT_IDLE_MAINTENANCE_MIN_BUDGET;  
  13.             // Reset the upcoming idle delays.  
  14.             mNextLightIdleDelay = mConstants.LIGHT_IDLE_TIMEOUT;  
  15.             mMaintenanceStartTime = 0;  
  16.             if (!isOpsInactiveLocked()) {  
  17.                 // We have some active ops going on...  give them a chance to finish  
  18.                 // before going in to our first idle.  
  19.                 mLightState = LIGHT_STATE_PRE_IDLE;  
  20.                 EventLogTags.writeDeviceIdleLight(mLightState, reason);  
  21.                 scheduleLightAlarmLocked(mConstants.LIGHT_PRE_IDLE_TIMEOUT);//设置alarm,时间到后到下个步骤  
  22.                 break;  
  23.             }  
  24.             // Nothing active, fall through to immediately idle.  
  25.         case LIGHT_STATE_PRE_IDLE:  
  26.         case LIGHT_STATE_IDLE_MAINTENANCE:  
  27.             if (mMaintenanceStartTime != 0) {  
  28.                 long duration = SystemClock.elapsedRealtime() - mMaintenanceStartTime;  
  29.                 if (duration < mConstants.LIGHT_IDLE_MAINTENANCE_MIN_BUDGET) {  
  30.                     // We didn't use up all of our minimum budget; add this to the reserve.  
  31.                     mCurIdleBudget += (mConstants.LIGHT_IDLE_MAINTENANCE_MIN_BUDGET-duration);  
  32.                 } else {  
  33.                     // We used more than our minimum budget; this comes out of the reserve.  
  34.                     mCurIdleBudget -= (duration-mConstants.LIGHT_IDLE_MAINTENANCE_MIN_BUDGET);  
  35.                 }  
  36.             }  
  37.             mMaintenanceStartTime = 0;  
  38.             scheduleLightAlarmLocked(mNextLightIdleDelay);  
  39.             mNextLightIdleDelay = Math.min(mConstants.LIGHT_MAX_IDLE_TIMEOUT,  
  40.                     (long)(mNextLightIdleDelay * mConstants.LIGHT_IDLE_FACTOR));  
  41.             if (mNextLightIdleDelay < mConstants.LIGHT_IDLE_TIMEOUT) {  
  42.                 mNextLightIdleDelay = mConstants.LIGHT_IDLE_TIMEOUT;  
  43.             }  
  44.             if (DEBUG) Slog.d(TAG, "Moved to LIGHT_STATE_IDLE.");  
  45.             mLightState = LIGHT_STATE_IDLE;  
  46.             EventLogTags.writeDeviceIdleLight(mLightState, reason);  
  47.             addEvent(EVENT_LIGHT_IDLE);  
  48.             mHandler.sendEmptyMessage(MSG_REPORT_IDLE_ON_LIGHT);//发送消息,这个消息处理就会关闭网络,禁止wakelock  
  49.             break;  
  50.         case LIGHT_STATE_IDLE:  
  51.         case LIGHT_STATE_WAITING_FOR_NETWORK:  
  52.             if (mNetworkConnected || mLightState == LIGHT_STATE_WAITING_FOR_NETWORK) {  
  53.                 // We have been idling long enough, now it is time to do some work.  
  54.                 mActiveIdleOpCount = 1;  
  55.                 mActiveIdleWakeLock.acquire();  
  56.                 mMaintenanceStartTime = SystemClock.elapsedRealtime();  
  57.                 if (mCurIdleBudget < mConstants.LIGHT_IDLE_MAINTENANCE_MIN_BUDGET) {  
  58.                     mCurIdleBudget = mConstants.LIGHT_IDLE_MAINTENANCE_MIN_BUDGET;  
  59.                 } else if (mCurIdleBudget > mConstants.LIGHT_IDLE_MAINTENANCE_MAX_BUDGET) {  
  60.                     mCurIdleBudget = mConstants.LIGHT_IDLE_MAINTENANCE_MAX_BUDGET;  
  61.                 }  
  62.                 scheduleLightAlarmLocked(mCurIdleBudget);  
  63.                 if (DEBUG) Slog.d(TAG,  
  64.                         "Moved from LIGHT_STATE_IDLE to LIGHT_STATE_IDLE_MAINTENANCE.");  
  65.                 mLightState = LIGHT_STATE_IDLE_MAINTENANCE;  
  66.                 EventLogTags.writeDeviceIdleLight(mLightState, reason);  
  67.                 addEvent(EVENT_LIGHT_MAINTENANCE);  
  68.                 mHandler.sendEmptyMessage(MSG_REPORT_IDLE_OFF);//醒一下(开启网络、恢复wakelock)  
  69.             } else {  
  70.                 // We'd like to do maintenance, but currently don't have network  
  71.                 // connectivity...  let's try to wait until the network comes back.  
  72.                 // We'll only wait for another full idle period, however, and then give up.  
  73.                 scheduleLightAlarmLocked(mNextLightIdleDelay);  
  74.                 if (DEBUG) Slog.d(TAG, "Moved to LIGHT_WAITING_FOR_NETWORK.");  
  75.                 mLightState = LIGHT_STATE_WAITING_FOR_NETWORK;  
  76.                 EventLogTags.writeDeviceIdleLight(mLightState, reason);  
  77.             }  
  78.             break;  
  79.     }  
  80. }  

但是这里只是一个light idle,一旦进入deep idle,light idle设置的alarm会无效的(这个后面细说),也就是说light idle一旦进入deep idle后无效了(因为idle step主要靠alarm驱动,而alarm无效后自然就驱动不了)。

deep idle模式

下面我们再来看deep idle模式,这个模式除了禁止网络、wakelock还会禁止alarm。

我们再来看becomeInactiveIfAppropriateLocked函数中下面代码。是关于deep idle的设置 这里的mInactiveTimeout是半小时

  1. void becomeInactiveIfAppropriateLocked() {  
  2.     if (DEBUG) Slog.d(TAG, "becomeInactiveIfAppropriateLocked()");  
  3.     if ((!mScreenOn && !mCharging) || mForceIdle) {  
  4.         // Screen has turned off; we are now going to become inactive and start  
  5.         // waiting to see if we will ultimately go idle.  
  6.         if (mState == STATE_ACTIVE && mDeepEnabled) {  
  7.             mState = STATE_INACTIVE;  
  8.             if (DEBUG) Slog.d(TAG, "Moved from STATE_ACTIVE to STATE_INACTIVE");  
  9.             resetIdleManagementLocked();  
  10.             scheduleAlarmLocked(mInactiveTimeout, false);  
  11.             EventLogTags.writeDeviceIdle(mState, "no activity");  
  12.         }  

我们来看下scheduleAlarmLocked函数,注意如果这里参数idleUntil是true会调用AlarmManager的setIdleUntil函数,调用这个函数后普通应用设置alarm将失效。

  1. void scheduleAlarmLocked(long delay, boolean idleUntil) {  
  2.     if (mMotionSensor == null) {  
  3.         //在onBootPhase时,获取过位置检测传感器  
  4.         //如果终端没有配置位置检测传感器,那么终端永远不会进入到真正的Doze ilde状态  
  5.         // If there is no motion sensor on this device, then we won't schedule  
  6.         // alarms, because we can't determine if the device is not moving.  
  7.         return;  
  8.     }  
  9.   
  10.     mNextAlarmTime = SystemClock.elapsedRealtime() + delay;  
  11.     if (idleUntil) {  
  12.         //此时IdleUtil的值为false  
  13.         mAlarmManager.setIdleUntil(AlarmManager.ELAPSED_REALTIME_WAKEUP,  
  14.                 mNextAlarmTime, "DeviceIdleController.deep", mDeepAlarmListener, mHandler);  
  15.     } else {  
  16.         //30min后唤醒,调用mDeepAlarmListener的onAlarm函数  
  17.         mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,  
  18.                 mNextAlarmTime, "DeviceIdleController.deep", mDeepAlarmListener, mHandler);  
  19.     }  
  20. }  

需要注意的是,DeviceIdleController一直在监控屏幕状态和充电状态,一但不满足Doze模式的条件,前面提到的becomeActiveLocked函数就会被调用。mAlarmManager设置的定时唤醒事件将被取消掉,mDeepAlarmListener的onAlarm函数不会被调用。

因此,我们知道了终端必须保持Doze模式的入口条件长达30min,才会进入mDeepAlarmListener.onAlarm:

  1. private final AlarmManager.OnAlarmListener mDeepAlarmListener  
  2.         = new AlarmManager.OnAlarmListener() {  
  3.     @Override  
  4.     public void onAlarm() {  
  5.         synchronized (DeviceIdleController.this) {  
  6.             //进入到stepIdleStateLocked函数  
  7.             stepIdleStateLocked("s:alarm");  
  8.         }  
  9.     }  
  10. };  

下面我们就来看下stepIdleStateLocked函数:

  1. void stepIdleStateLocked(String reason) {  
  2.     ..........  
  3.     final long now = SystemClock.elapsedRealtime();  
  4.     //个人觉得,下面这段代码,是针对Idle状态设计的  
  5.     //如果在Idle状态收到Alarm,那么将先唤醒终端,然后重新判断是否需要进入Idle态  
  6.     //在介绍Doze模式原理时提到过,若应用调用AlarmManager的一些指定接口,仍然可以在Idle状态进行工作  
  7.     if ((now+mConstants.MIN_TIME_TO_ALARM) > mAlarmManager.getNextWakeFromIdleTime()) {  
  8.         // Whoops, there is an upcoming alarm.  We don't actually want to go idle.  
  9.         if (mState != STATE_ACTIVE) {  
  10.             becomeActiveLocked("alarm", Process.myUid());  
  11.             becomeInactiveIfAppropriateLocked();  
  12.         }  
  13.         return;  
  14.     }  
  15.   
  16.     //以下是Doze模式的状态转变相关的代码  
  17.     switch (mState) {  
  18.         case STATE_INACTIVE:  
  19.             // We have now been inactive long enough, it is time to start looking  
  20.             // for motion and sleep some more while doing so.  
  21.             //保持屏幕熄灭,同时未充电达到30min,进入此分支  
  22.   
  23.             //注册一个mMotionListener,检测是否移动  
  24.             //如果检测到移动,将重新进入到ACTIVE状态  
  25.             //相应代码比较直观,此处不再深入分析  
  26.             startMonitoringMotionLocked();  
  27.   
  28.             //再次调用scheduleAlarmLocked函数,此次的时间仍为30min  
  29.             //也就说如果不发生退出Doze模式的事件,30min后将再次进入到stepIdleStateLocked函数  
  30.             //不过届时的mState已经变为STATE_IDLE_PENDING  
  31.             scheduleAlarmLocked(mConstants.IDLE_AFTER_INACTIVE_TIMEOUT, false);  
  32.   
  33.             // Reset the upcoming idle delays.  
  34.             //mNextIdlePendingDelay为5min  
  35.             mNextIdlePendingDelay = mConstants.IDLE_PENDING_TIMEOUT;  
  36.             //mNextIdleDelay为60min  
  37.             mNextIdleDelay = mConstants.IDLE_TIMEOUT;  
  38.   
  39.             //状态变为STATE_IDLE_PENDING   
  40.             mState = STATE_IDLE_PENDING;  
  41.             ............  
  42.             break;  
  43.         case STATE_IDLE_PENDING:  
  44.             //保持息屏、未充电、静止状态,经过30min后,进入此分支  
  45.             mState = STATE_SENSING;  
  46.   
  47.             //保持Doze模式条件,4min后再次进入stepIdleStateLocked  
  48.             scheduleSensingTimeoutAlarmLocked(mConstants.SENSING_TIMEOUT);  
  49.   
  50.             //停止定位相关的工作  
  51.             cancelLocatingLocked();  
  52.             mNotMoving = false;  
  53.             mLocated = false;  
  54.             mLastGenericLocation = null;  
  55.             mLastGpsLocation = null;  
  56.   
  57.             //开始检测手机是否发生运动(这里应该是更细致的侧重于角度的变化)  
  58.             //若手机运动过,则重新变为active状态  
  59.             mAnyMotionDetector.checkForAnyMotion();  
  60.             break;  
  61.         case STATE_SENSING:  
  62.             //上面的条件满足后,进入此分支,开始获取定位信息  
  63.             cancelSensingTimeoutAlarmLocked();  
  64.             mState = STATE_LOCATING;  
  65.             ............  
  66.             //保持条件30s,再次调用stepIdleStateLocked  
  67.             scheduleAlarmLocked(mConstants.LOCATING_TIMEOUT, false);  
  68.   
  69.             //网络定位  
  70.             if (mLocationManager != null  
  71.                     && mLocationManager.getProvider(LocationManager.NETWORK_PROVIDER) != null) {  
  72.                 mLocationManager.requestLocationUpdates(mLocationRequest,  
  73.                         mGenericLocationListener, mHandler.getLooper());  
  74.                 mLocating = true;  
  75.             } else {  
  76.                 mHasNetworkLocation = false;  
  77.             }  
  78.   
  79.             //GPS定位  
  80.             if (mLocationManager != null  
  81.                     && mLocationManager.getProvider(LocationManager.GPS_PROVIDER) != null) {  
  82.                 mHasGps = true;  
  83.                 mLocationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 1000, 5,  
  84.                         mGpsLocationListener, mHandler.getLooper());  
  85.                 mLocating = true;  
  86.             } else {  
  87.                 mHasGps = false;  
  88.             }  
  89.   
  90.             // If we have a location provider, we're all set, the listeners will move state  
  91.             // forward.  
  92.             if (mLocating) {  
  93.                 //无法定位则直接进入下一个case  
  94.                 break;  
  95.             }  
  96.         case STATE_LOCATING:  
  97.             //停止定位和运动检测,直接进入到STATE_IDLE_MAINTENANCE  
  98.             cancelAlarmLocked();  
  99.             cancelLocatingLocked();  
  100.             mAnyMotionDetector.stop();  
  101.   
  102.         case STATE_IDLE_MAINTENANCE:  
  103.             //进入到这个case后,终端开始进入Idle状态,也就是真正的Doze模式  
  104.   
  105.             //定义退出Idle的时间此时为60min  
  106.             scheduleAlarmLocked(mNextIdleDelay, true);  
  107.             ............  
  108.             //退出周期逐步递增,每次乘2  
  109.             mNextIdleDelay = (long)(mNextIdleDelay * mConstants.IDLE_FACTOR);  
  110.             ...........  
  111.             //周期有最大值6h  
  112.             mNextIdleDelay = Math.min(mNextIdleDelay, mConstants.MAX_IDLE_TIMEOUT);  
  113.             if (mNextIdleDelay < mConstants.IDLE_TIMEOUT) {  
  114.                 mNextIdleDelay = mConstants.IDLE_TIMEOUT;  
  115.             }  
  116.   
  117.             mState = STATE_IDLE;  
  118.             ...........  
  119.             //通知PMS、NetworkPolicyManagerService等Doze模式开启,即进入Idle状态  
  120.             //此时PMS disable一些非白名单WakeLock;NetworkPolicyManagerService开始限制一些应用的网络访问  
  121.             //消息处理的具体流程比较直观,此处不再深入分析  
  122.             mHandler.sendEmptyMessage(MSG_REPORT_IDLE_ON);  
  123.             break;  
  124.   
  125.         case STATE_IDLE:  
  126.             //进入到这个case时,本次的Idle状态暂时结束,开启maintenance window  
  127.   
  128.             // We have been idling long enough, now it is time to do some work.  
  129.             mActiveIdleOpCount = 1;  
  130.             mActiveIdleWakeLock.acquire();  
  131.   
  132.             //定义重新进入Idle的时间为5min (也就是手机可处于Maintenance window的时间)  
  133.             scheduleAlarmLocked(mNextIdlePendingDelay, false);  
  134.   
  135.             mMaintenanceStartTime = SystemClock.elapsedRealtime();  
  136.             //调整mNextIdlePendingDelay,乘2(最大为10min)  
  137.             mNextIdlePendingDelay = Math.min(mConstants.MAX_IDLE_PENDING_TIMEOUT,  
  138.                     (long)(mNextIdlePendingDelay * mConstants.IDLE_PENDING_FACTOR));  
  139.   
  140.             if (mNextIdlePendingDelay < mConstants.IDLE_PENDING_TIMEOUT) {  
  141.                     mNextIdlePendingDelay = mConstants.IDLE_PENDING_TIMEOUT;  
  142.             }  
  143.   
  144.             mState = STATE_IDLE_MAINTENANCE;  
  145.             ...........  
  146.             //通知PMS等暂时退出了Idle状态,可以进行一些工作  
  147.             //此时PMS enable一些非白名单WakeLock;NetworkPolicyManagerService开始允许应用的网络访问  
  148.             mHandler.sendEmptyMessage(MSG_REPORT_IDLE_OFF);  
  149.             break;  
  150.     }  
  151. }  

上面的流程在注释里面已经很明白了,而我们在进入Deep idle时,发送了一个MSG_REPORT_IDLE_ON消息,我们看下面这个消息的处理和之前的MSG_REPORT_IDLE_ON_LIGHT一样的,关闭网络,禁止wakelock。

  1. case MSG_REPORT_IDLE_ON:  
  2. case MSG_REPORT_IDLE_ON_LIGHT: {  
  3.     EventLogTags.writeDeviceIdleOnStart();  
  4.     final boolean deepChanged;  
  5.     final boolean lightChanged;  
  6.     if (msg.what == MSG_REPORT_IDLE_ON) {  
  7.         deepChanged = mLocalPowerManager.setDeviceIdleMode(true);  
  8.         lightChanged = mLocalPowerManager.setLightDeviceIdleMode(false);  
  9.     } else {  
  10.         deepChanged = mLocalPowerManager.setDeviceIdleMode(false);  
  11.         lightChanged = mLocalPowerManager.setLightDeviceIdleMode(true);  
  12.     }  
  13.     try {  
  14.         mNetworkPolicyManager.setDeviceIdleMode(true);  
  15.         mBatteryStats.noteDeviceIdleMode(msg.what == MSG_REPORT_IDLE_ON  
  16.                 ? BatteryStats.DEVICE_IDLE_MODE_DEEP  
  17.                 : BatteryStats.DEVICE_IDLE_MODE_LIGHT, null, Process.myUid());  
  18.     } catch (RemoteException e) {  
  19.     }  
  20.     if (deepChanged) {  
  21.         getContext().sendBroadcastAsUser(mIdleIntent, UserHandle.ALL);  
  22.     }  
  23.     if (lightChanged) {  
  24.         getContext().sendBroadcastAsUser(mLightIdleIntent, UserHandle.ALL);  
  25.     }  
  26.     EventLogTags.writeDeviceIdleOnComplete();  
  27. break;  

而禁止alarm是通过调用如下函数,注意参数是true。参数是true会调用mAlarmManager.setIdleUntil函数。这样其他的alarm会被滞后(除非在白名单中)

  1. scheduleAlarmLocked(mNextIdleDelay, true);  

而每隔一段时间会进入Maintenance window的时间,此时是通过发送MSG_REPORT_IDLE_OFF消息,来恢复网络和wakelock。而这个时候之前设置的mAlarmManager.setIdleUntil的alarm也到期了,因此其他alarm也恢复了。但是这个时间只有5分钟,重新设置了alarm再次进入deep idle状态。

Idle总结

当手机关闭屏幕或者拔掉电源的时候,手机开始判断是否进入Doze模式。

Doze模式分两种,第一种是light idle:

1.light idle

light idle在手机灭屏且没有充电状态下,5分钟开始进入light idle流程。然后第一次进入LIGHT_STATE_INACTIVE流程时,会再定义一个10分钟的alarm。然后系统进入light idle状态。这个状态会使不是白名单的应用禁止访问网络,以及持wakelock锁。

2.deep idle

deep idle除了light idle的状态还会把非白名单中应用的alarm也禁止了。
 此时,系统中非白名单的应用将被禁止访问网络,它们申请的Wakelock也会被disable。
 从上面的代码可以看出,系统会周期性的退出Idle状态,进入到MAINTENANCE状态,集中处理相关的任务。
 一段时间后,会重新再次回到IDLE状态。每次进入IDLE状态,停留的时间都会是上次的2倍,最大时间限制为6h。

当手机运动,或者点亮屏幕,插上电源等,系统都会重新返回到ACTIVIE状态。

这里盗用别人的一样图,但仅仅是deep idle的状态:

(这里特别说明下,alarm和wakelock都是由DeviceIdleController主动调用相关接口设置的,而网络是调用了DeviceIdleController的getAppIdWhitelist接口来获取应用的白名单的,从而禁止非白名单访问网络。)


网络我们不分析了,之前我们在Android6.0时分析过idle状态下alarm和wakelock,7.0稍微有点不一样,下面两篇博客重新分析下吧。


转载图有错,如下是我纠正的图:


  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值