【Android 低电耗/Doze原理—设备运动状态和位置对Doze模式的影响】
Android 低电耗/Doze原理---设备运动状态和位置对Doze模式的影响
基础知识
Android 6.0以后引入的Doze机制(也叫低电耗模式)在于节省系统耗电量,延长设备的使用时间。当设备息屏,未连接至电源,且长时间处于静置状态时,系统进入idle状态,也就是doze状态。
Doze模式分为Light Idle(Android 7.0引入)和Deep Idle。设备进入两种状态花费的时长不一样,且对应用的限制程度也不一样。
本文基于Android 12代码讲述的是DeviceidleController通过哪些方式判断设备处于静置状态,以及运动状态和位置对设备进入Deep Idle过程的影响。
Deep Idle的状态机
Deep Idle中的状态机主要有以下六种。
/** Device is currently active. */
static final int STATE_ACTIVE = 0;
/** Device is inactive (screen off, no motion) and we are waiting to for idle. */
static final int STATE_INACTIVE = 1;
/** Device is past the initial inactive period, and waiting for the next idle period. */
static final int STATE_IDLE_PENDING = 2;
/** Device is currently sensing motion. */
static final int STATE_SENSING = 3;
/** Device is currently finding location (and may still be sensing). */
static final int STATE_LOCATING = 4;
/** Device is in the idle state, trying to stay asleep as much as possible. */
static final int STATE_IDLE = 5;
/** Device is in the idle state, but temporarily out of idle to do regular maintenance. */
static final int STATE_IDLE_MAINTENANCE = 6;
进入Deep Idle过程中,对于位置和运行状态的检测逻辑在STATE_IDLE_PENDING 、STATE_SENSING、STATE_LOCATING实现。以下基于这三种状态机进行讲解。
状态 | 动作 | 状态持续时长 |
---|---|---|
STATE_ACTIVE | / | / |
STATE_INACTIVE | / | 30mins |
STATE_IDLE_PENDING | 监听运动状态变化 | 30mins |
STATE_SENSING | 晃动检测 | 最多5mins,通常为3s |
STATE_LOCATING | 停止晃动检测,监听位置变化 | 30s |
STATE_IDLE | 断网、限制alarm、禁止wifi scan… | 最短1hour,最长6hours |
STATE_IDLE_MAINTENANCE | 恢复网络、取消alarm限制、允许wifi scan… | 至少30s |
STATE_IDLE_PENDING----监听运动状态变化
状态机进入到STATE_IDLE_PENDING,调用startMonitoringMotionLocked监听运动状态变化。监听运动状态变化过程是一直持续的。
下面两种情况时,会停止监听:
- 当状态机mState变成ACTIVE 或者STATE_INACTIVE
- 运动状态变化,回调函数onSensorChanged / onTrigger调用handleMotionDetectedLocked
void startMonitoringMotionLocked() {
if (DEBUG) Slog.d(TAG, "startMonitoringMotionLocked()");
if (mMotionSensor != null && !mMotionListener.active) {
mMotionListener.registerLocked();
}
}
在DeviceidleController的内部MotionListener类中根据sensor类型注册监听。当运动状态变化时,不同的sensor类型对应不同的回调函数。
public boolean registerLocked() {
boolean success;
//根据初始化时 获取到mMotionSensor的sensor类型,决定应该调用监听运动状态的方法
if (mMotionSensor.getReportingMode() == Sensor.REPORTING_MODE_ONE_SHOT) {
//运动状态变化时,回调onTrigger方法
success = mSensorManager.requestTriggerSensor(mMotionListener, mMotionSensor);
} else {
//运动状态变化时,回调onSensorChanged方法
success = mSensorManager.registerListener(
mMotionListener, mMotionSensor, SensorManager.SENSOR_DELAY_NORMAL);
}
if (success) {
active = true;
activatedTimeElapsed = mInjector.getElapsedRealtime();
} else {
Slog.e(TAG, "Unable to register for " + mMotionSensor);
}
return success;
}
当运动状态变化时,onSensorChanged和onTrigger方法都调用到motionLocked方法,进而在handleMotionDetectedLocked实现具体的逻辑。
- 当有别的模块监听系统运动状态变化(mStationaryListeners个数>0)时:
- 向listener们通知当前系统运动变化情况;
- 设置10mins后再次更新
- 设置alarm,5-6mins后再重新开始监听运行状态
- 调用maybeStopMonitoringMotionLocked,停止监听运行状态
- 重新开始Deep Idle状态机循环,将当前状态mState 置为ACTIVE
void motionLocked() {
if (DEBUG) Slog.d(TAG, "motionLocked()");
mLastMotionEventElapsed = mInjector.getElapsedRealtime();
handleMotionDetectedLocked(mConstants.MOTION_INACTIVE_TIMEOUT, "motion");
}
void handleMotionDetectedLocked(long timeout, String type) {
if (mStationaryListeners.size() > 0) {
//向listener们通知当前系统运动变化情况
postStationaryStatusUpdated();
//10mins后再次通报
scheduleMotionTimeoutAlarmLocked();
//5-6mins后再重新注册运行状态监听器,并重新开始监听.
scheduleMotionRegistrationAlarmLocked();
}
if (mQuickDozeActivated && !mQuickDozeActivatedWhileIdling) {
return;
}
//会调用unregisterLocked,停止运行状态监听
maybeStopMonitoringMotionLocked();
final boolean becomeInactive = mState != STATE_ACTIVE
|| mLightState == LIGHT_STATE_OVERRIDE;
//重新开始Deep Idle状态机循环,将当前状态置为ACTIVE
becomeActiveLocked(type, Process.myUid(), timeout, mLightState == LIGHT_STATE_OVERRIDE);
if (becomeInactive) {
becomeInactiveIfAppropriateLocked();
}
}
STATE_SENSING ----晃动检测
当状态机在STATE_IDLE_PENDING阶段已经进行了30mins的运动检测后(不满30mins退出,则重新开始30mins计时),才能进入STATE_SENSING阶段,并开始晃动检测。
晃动检测是通过加速度传感器采集相应数据实现的。如果当前设备没有加速度传感器,那么跳过该阶段(默认没有晃动),直接进入到STATE_LOCATING,开始监听位置变化情况。否则在AnyMotionDetector#checkForAnyMotion开始采集数据,并判断当前设备是否晃动,将检测结果通过onAnyMotionResult函数通知到DeviceidleController。
stepIdleStateLocked{
.....
if (mUseMotionSensor && mAnyMotionDetector.hasSensor()) {
//最多允许4-5mins的晃动检测,超时则调用becomeInactiveIfAppropriateLocked
scheduleSensingTimeoutAlarmLocked(mConstants.SENSING_TIMEOUT);
mNotMoving = false;
//开始晃动检测,检测结束后,会回调onAnyMotionResult
mAnyMotionDetector.checkForAnyMotion();
break;
} else if (mNumBlockingConstraints != 0) {
cancelAlarmLocked();
break;
}
mNotMoving = true;
.....
}
onAnyMotionResult内根据检测结果:
- 当前设备晃动,则通过handleMotionDetectedLocked,重新开始Deep Idle状态机循环,将当前状态置为ACTIVE。
- 当前设备不晃动,通过stepIdleStateLocked进入到下一个状态机。
public void onAnyMotionResult(int result) {
if (DEBUG) Slog.d(TAG, "onAnyMotionResult(" + result + ")");
if (result != AnyMotionDetector.RESULT_UNKNOWN) {
synchronized (this) {
//取消alarm的4-5mins定时
cancelSensingTimeoutAlarmLocked();
}
}
if ((result == AnyMotionDetector.RESULT_MOVED) ||
(result == AnyMotionDetector.RESULT_UNKNOWN)) {
synchronized (this) {
handleMotionDetectedLocked(mConstants.INACTIVE_TIMEOUT, "non_stationary");
}
} else if (result == AnyMotionDetector.RESULT_STATIONARY) {
if (mState == STATE_SENSING) {
// If we are currently sensing, it is time to move to locating.
synchronized (this) {
mNotMoving = true;
stepIdleStateLocked("s:stationary");
}
} else if (mState == STATE_LOCATING) {
// If we are currently locating, note that we are not moving and step
// if we have located the position.
synchronized (this) {
mNotMoving = true;
if (mLocated) {
stepIdleStateLocked("s:stationary");
}
}
}
}
}
STATE_LOCATING ----监听设备位置变化
状态机在STATE_SENSING阶段晃动检测,确认设备处于静置状态,那么会进入到STATE_LOCATING,通过网络定位和GPS定位开始监听位置变化。
此过程持续30s,如果30s内位置没发生变化,那么就状态机进入idle状态,也就是Deep Idle。
stepIdleStateLocked{
.......
moveToStateLocked(STATE_LOCATING, reason);
//30s位置变化检测
scheduleAlarmLocked(mConstants.LOCATING_TIMEOUT, false);
LocationManager locationManager = mInjector.getLocationManager();
//获取网络位置信息更新接口
if (locationManager != null
&& locationManager.getProvider(LocationManager.NETWORK_PROVIDER) != null) {
//如果位置变化,通知到mGenericLocationListener
locationManager.requestLocationUpdates(mLocationRequest,
mGenericLocationListener, mHandler.getLooper());
mLocating = true;
} else {
mHasNetworkLocation = false;
}
//获取GPS方式的位置信息
if (locationManager != null
&& locationManager.getProvider(LocationManager.GPS_PROVIDER) != null) {
mHasGps = true;
//如果位置变化,通知到mGpsLocationListener
locationManager.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) {
break;
}
....
}
mGenericLocationListener----网络定位监听
位置出现变化时,
- 状态机 不是 STATE_LOCATING,取消全部定位监听
- 状态机 是 STATE_LOCATING,通过stepIdleStateLocked进入下一状态(idle)
void receivedGenericLocationLocked(Location location) {
if (mState != STATE_LOCATING) {
cancelLocatingLocked();
return;
}
if (DEBUG) Slog.d(TAG, "Generic location: " + location);
mLastGenericLocation = new Location(location);
if (location.getAccuracy() > mConstants.LOCATION_ACCURACY && mHasGps) {
return;
}
mLocated = true;
if (mNotMoving) {
stepIdleStateLocked("s:location");
}
}
mGpsLocationListener----GPS定位监听
逻辑和mGenericLocationListener一致。
void receivedGpsLocationLocked(Location location) {
if (mState != STATE_LOCATING) {
cancelLocatingLocked();
return;
}
if (DEBUG) Slog.d(TAG, "GPS location: " + location);
mLastGpsLocation = new Location(location);
if (location.getAccuracy() > mConstants.LOCATION_ACCURACY) {
return;
}
mLocated = true;
if (mNotMoving) {
stepIdleStateLocked("s:gps");
}
}
总结
位置监听,运动状态监听、晃动检测的持续时长和doze状态机之间的关系:
运动状态监听:从STATE_IDLE_PENDING — > STATE_IDLE_MAINTENANCE一直都在监听。如果设备已经进入Deep Idle,也会因为运动检测退出idle。
晃动检测:当状态机为STATE_SENSING,通过加速度传感器进行检测。
位置监听:在LOCATING阶段,通过网络定位和gps定位进行监听。