【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实现具体的逻辑。

  1. 当有别的模块监听系统运动状态变化(mStationaryListeners个数>0)时:
    • 向listener们通知当前系统运动变化情况;
    • 设置10mins后再次更新
    • 设置alarm,5-6mins后再重新开始监听运行状态
  2. 调用maybeStopMonitoringMotionLocked,停止监听运行状态
  3. 重新开始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定位进行监听。


版权声明:本文为Mr_xuzhu原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
THE END
< <上一篇
下一篇>>