Android6.0以后引入了Doze和Standby省电模式,看了下源码,主要体现在DeviceIdle服务中。
DeviceIdle采用了定时器驱动的状态机方式来推进不同状态之间的变化。状态变化如下图:
下面对照图详细展开来说。
先看一下nexus6p实际dumpsys的结果:
# dumpsys deviceidle
Settings:
inactive_to=+30m0s0ms
sensing_to=+60s0ms
locating_to=+15s0ms
location_accuracy=20.0m
motion_inactive_to=+10m0s0ms
idle_after_inactive_to=+30m0s0ms
idle_pending_to=+5m0s0ms
max_idle_pending_to=+10m0s0ms
idle_pending_factor=2.0
idle_to=+60m0s0ms
max_idle_to=+6h0m0s0ms
idle_factor=2.0
min_time_to_alarm=+60m0s0ms
max_temp_app_whitelist_duration=+5m0s0ms
mms_temp_app_whitelist_duration=+60s0ms
sms_temp_app_whitelist_duration=+20s0ms
Whitelist (except idle) system apps:
com.android.providers.downloads
Whitelist (except idle) all app ids:
10006
mEnabled=false
mForceIdle=false
mSigMotionSensor={Sensor name="Significant motion", vendor="Google", version=1, type=17, maxRange=1.0, resolution=1.0, power=0.0, minDelay=-1}
mCurDisplay=Display id 0: DisplayInfo{"内置屏幕", uniqueId "local:0", app 1440 x 2392, real 1440 x 2560, largest app 2392 x 2308, smallest app 1440 x 1356, mode 1, defaultMode 1, modes [{id=1, width=1440, height=2560, fps=60.0}], colorTransformId 1, defaultColorTransformId 1, supportedColorTransforms [{id=1, colorTransform=-22}], rotation 0, density 560 (515.154 x 516.063) dpi, layerStack 0, appVsyncOff 0, presDeadline 17666667, type BUILT_IN, state ON, FLAG_SECURE, FLAG_SUPPORTS_PROTECTED_BUFFERS}, DisplayMetrics{density=3.5, width=1440, height=2392, scaledDensity=3.5, xdpi=515.154, ydpi=516.063}, isValid=true
mScreenOn=true
mCharging=true
mSigMotionActive=false
mSensing=false mNotMoving=false
mLocating=false mHaveGps=false mLocated=false
mState=ACTIVE
mInactiveTimeout=+30m0s0ms
结果中输出了很多定时器的时间,这些时间也体现在上图中。
状态切换
Active
DeviceIdle中注册了电源和显示器的监听器,当处于充电或者点亮屏幕时,就激活此状态,另外此状态还会被Monition Listener激活,这个后面会说。InActive
当处于未充电且熄灭屏幕时,立刻进入此状态。此状态会持续一段时间,之后超时进入下一个状态。超时时间inactive_timeout在6p上面是30分钟。Idle pending
InActive超时后进入此状态,此状态会持续一段时间,之后超时进入下一个状态。超时时间idle_after_inactive_timetout在6p上面是30分钟。
期间会启动对significant monition的监听,这个监听在后面的状态中一直存在。
significant monition监听器检测到设备移动后,会立刻先进入Active状态,再检测电源状态和屏幕状态,如果满足InActive条件,立即进入InActive状态。Sensing
Idle pending超时后进入此状态,此状态会持续一段时间,之后超时进入下一个状态。超时时间sensing_timeout在6p上是60秒。
期间会启动Any monition Detector,如果检测到有移动,同样会进入Active状态,和上面描述一致。Locating
Sensing超时进入此状态,此状态会持续一段时间,之后超时进入下一个状态。超时时间locating_timeout在6p上是15秒。
期间会发起generic location和gps location(结果优先),同时会停止上一步启动的any moniton detector。如果location返回结果表明没有移动,则不等待locating_timeout超时直接进入下一个状态。
Locating在最后阶段会结束前面发起的generic location 和gps location。Idle
先由Locating进入,代码中对应的case分支没有break,所以会执行到下一个case中。图中单独画了一个“time point”,其实是Locating状态下最后时间段做的事情。
idle会和idle maintenance形成循环切换,从而形成交替打开的执行窗口。
Idle状态会禁止应用的网络访问等,idle maintenance则是临时打开的活动窗口,应用可以进行网络访问。Idle的持续时间在6p上最初是60分钟,之后会逐渐变长,计算公式为:min(6小时,idleDelay*factor)。 其中factor为2,idleDelay为上一次持续时间。Idle maintenance
和Idle形成执行窗口。持续时间在6p上最初是5分钟,之后会逐渐变长,计算公式为:min(10分钟,idlePendingDelay*factor)。其中factor为2,idlePendingDelay为上一次持续时间。
总结
最核心的状态就是Idle和Idle maintenance,在这个两个状态发生切换时,会通知其他服务,比如powerManager, networkManger,进行相应的服务限制或者放行。