Android屏幕旋转流程(2)

(1)疑问

(1)settings put system user_rotation 1是什么意思?

答:设置用户期望的屏幕转向,0代表:Surface.ROTATION_0竖屏;1代表:Surface.ROTATION_90横屏;

(2)设置user_rotation和GSensor哪个优先级更高,比如user_rotation = 0期待竖屏,但是打开屏幕旋转且处于横屏时,应该是横屏还是竖屏?

答:此时GSensor优先级更高,横屏显示,具体原因看第三个问题。

(3)SystemUI中的"自动旋转"按钮影响的是哪个数据和系统的值?

答:会影响Settings.System.ACCELEROMETER_ROTATION和DisplayRotation.userRotationMode的值。

java 复制代码
// When not otherwise specified by the activity's screenOrientation, rotation should be determined by the system (that is, using sensors).
public final int USER_ROTATION_FREE = 0;

//When not otherwise specified by the activity's screenOrientation, rotation is set by the user.
public final int USER_ROTATION_LOCKED = 1;

USER_ROTATION_FREE :如果应用不指定屏幕方向,sensor传感器决定
USER_ROTATION_LOCKED:如果应用不指定屏幕方向,user决定方向,即user_rotation数据库值

打开自动旋转时候设置的是Settings.System.ACCELEROMETER_ROTATION的值,并且为1,否则设置成0,这个值会直接影响DisplayRotation.userRotationMode的值。

java 复制代码
final int userRotationMode = Settings.System.getIntForUser(resolver,
       Settings.System.ACCELEROMETER_ROTATION, 0, UserHandle.USER_CURRENT) !=0
       ? WindowManagerPolicy.USER_ROTATION_FREE
       : WindowManagerPolicy.USER_ROTATION_LOCKED;

也就说如果打开了自动旋转,userRotationMode = USER_ROTATION_FREE,代表通过sensor决定;

否则设置成USER_ROTATION_LOCKED,由user_rotation决定。

(2)屏幕旋转流程

在Framework中,屏幕旋转功能主要是由WMS模块中的DisplayRotation对象来完成,在启动WindowManagerService过程中,创建DisplayContent对象时,会创建一个对应的DisplayRotation负责屏幕旋转逻辑,一个DisiplayContent对象对应一个DisplayRotation对象,或者说DisplayContent对象中持有一个DisplayRotation对象。

在DisplayRotation中,将获取Sensor数据并转换成具体方向旋转角度值的逻辑交给了OrientationListener对象来负责。

总结如下:DisplayContent负责控制,DisplayRotation负责执行,OrientationListener负责获取数据。

(A)DisplayRotation的初始化

java 复制代码
//frameworks/base/services/core/java/com/android/server/wm/DisplayRotation.java
    DisplayRotation(WindowManagerService service, DisplayContent displayContent,
            DisplayPolicy displayPolicy, DisplayWindowSettings displayWindowSettings,
            Context context, Object lock) {
        // 是否支持自动旋转
        mSupportAutoRotation =
                mContext.getResources().getBoolean(R.bool.config_supportAutoRotation);
        // lid open 时指定的旋转角度
        mLidOpenRotation = readRotation(R.integer.config_lidOpenRotation);
        // 放在car dock时指定的旋转角度
        mCarDockRotation = readRotation(R.integer.config_carDockRotation);
        // 放在desk dock时指定的旋转角度
        mDeskDockRotation = readRotation(R.integer.config_deskDockRotation);
        // Hdmi连接时指定的旋转角度
        mUndockedHdmiRotation = readRotation(R.integer.config_undockedHdmiRotation);

        if (isDefaultDisplay) {
            final Handler uiHandler = UiThread.getHandler();
            // 创建OrientationListener对象
            mOrientationListener = new OrientationListener(mContext, uiHandler);
            // 初始化
            mOrientationListener.setCurrentRotation(mRotation);
            // 监听SettingsProvider中的变化
            mSettingsObserver = new SettingsObserver(uiHandler);
            mSettingsObserver.observe();
        }
    }

屏幕旋转离不开Sensor的监听,具体是由OrientationListener来负责,它会获取Sensor对象、监听Sensor数据、将Sensor的数据转换成旋转角度,并通知WindowManagerService更新方向。

(B)OrientationListener监听Gsensor数据

java 复制代码
//frameworks/base/services/core/java/com/android/server/wm/DisplayRotation.java
private class OrientationListener extends WindowOrientationListener implements Runnable {
        transient boolean mEnabled;

        OrientationListener(Context context, Handler handler,
                @Surface.Rotation int defaultRotation) {
            super(context, handler, defaultRotation);
        }

        @Override
        public void onProposedRotationChanged(@Surface.Rotation int rotation) {
            ProtoLog.v(WM_DEBUG_ORIENTATION, "onProposedRotationChanged, rotation=%d", rotation);
            // Send interaction power boost to improve redraw performance.
            mService.mPowerManagerInternal.setPowerBoost(Boost.INTERACTION, 0);
            dispatchProposedRotation(rotation);
            if (isRotationChoiceAllowed(rotation)) {
                mRotationChoiceShownToUserForConfirmation = rotation;
                final boolean isValid = isValidRotationChoice(rotation);
                sendProposedRotationChangeToStatusBarInternal(rotation, isValid);
            } else {
                mRotationChoiceShownToUserForConfirmation = ROTATION_UNDEFINED;
                mService.updateRotation(false /* alwaysSendConfiguration */,
                        false /* forceRelayout */);
            }
        }

        @Override
        public void enable() {
            mEnabled = true;
            getHandler().post(this);
            ProtoLog.v(WM_DEBUG_ORIENTATION, "Enabling listeners");
        }

        @Override
        public void disable() {
            mEnabled = false;
            getHandler().post(this);
            ProtoLog.v(WM_DEBUG_ORIENTATION, "Disabling listeners");
        }

        @Override
        public void run() {
            if (mEnabled) {
                super.enable();
            } else {
                super.disable();
            }
        }
    }

我们看一下它的父类WindowOrientationListener相关。

java 复制代码
//frameworks/base/services/core/java/com/android/server/wm/WindowOrientationListener.java
public abstract class WindowOrientationListener {

	public WindowOrientationListener(Context context, Handler handler,
            @Surface.Rotation int defaultRotation) {
        this(context, handler, defaultRotation, SensorManager.SENSOR_DELAY_UI);
    }

	private WindowOrientationListener(Context context, Handler handler,
            @Surface.Rotation int defaultRotation, int rate) {
        mContext = context;
        mHandler = handler;
        mDefaultRotation = defaultRotation;
        mSensorManager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE);
        mRate = rate;
        List<Sensor> l = mSensorManager.getSensorList(Sensor.TYPE_DEVICE_ORIENTATION);
        Sensor wakeUpDeviceOrientationSensor = null;
        Sensor nonWakeUpDeviceOrientationSensor = null;

        for (Sensor s : l) {
            if (s.isWakeUpSensor()) {
                wakeUpDeviceOrientationSensor = s;
            } else {
                nonWakeUpDeviceOrientationSensor = s;
            }
        }

        if (wakeUpDeviceOrientationSensor != null) {
            mSensor = wakeUpDeviceOrientationSensor;
        } else {
            mSensor = nonWakeUpDeviceOrientationSensor;
        }

        if (mSensor != null) {
        	//优先使用此种Sensor监听
            mOrientationJudge = new OrientationSensorJudge();
        }

        if (mOrientationJudge == null) {
            mSensor = mSensorManager.getDefaultSensor(USE_GRAVITY_SENSOR
                    ? Sensor.TYPE_GRAVITY : Sensor.TYPE_ACCELEROMETER);
            if (mSensor != null) {
                // Create listener only if sensors do exist
                //还有另外一种选择
                mOrientationJudge = new AccelSensorJudge(context);
            }
        }
    }

	 abstract boolean isRotationResolverEnabled();
	 public abstract void onProposedRotationChanged(int rotation);
}

OrientationSensorJudge和AccelSensorJudge都是继承自OrientationJudge,而OrientationJudge类作为SensorEventListener的实现类来接收Sensor事件,根据不同的Sensor会有不同的OrientationJudge对象与之匹配,之所以这样做是因为不同的Sensor上报的原始数据不同,因此需要做不同的转换才能获得最终的旋转角度值。

java 复制代码
//frameworks/base/services/core/java/com/android/server/wm/WindowOrientationListener.java
abstract class OrientationJudge implements SensorEventListener {
		//...
        @Override
        public abstract void onAccuracyChanged(Sensor sensor, int accuracy);

        @Override
        public abstract void onSensorChanged(SensorEvent event);
    }
java 复制代码
final class OrientationSensorJudge extends OrientationJudge {

	public void onSensorChanged(SensorEvent event) {
            int reportedRotation = (int) event.values[0];
            if (reportedRotation < 0 || reportedRotation > 3) {
                return;
            }
			//...
            finalizeRotation(reportedRotation);
        }

        @Override
        public void onAccuracyChanged(Sensor sensor, int accuracy) { }
}

private void finalizeRotation(int reportedRotation) {
            int newRotation;
            synchronized (mLock) {
                mDesiredRotation = reportedRotation;
                newRotation = evaluateRotationChangeLocked();
            }
            if (newRotation >= 0) {
                mLastRotationResolution = newRotation;
                mLastRotationResolutionTimeStamp = SystemClock.uptimeMillis();
                //最终回调子类实现的onProposedRotationChanged函数
                onProposedRotationChanged(newRotation);
            }
        }

最终调用到OrientationListener的onProposedRotationChanged函数。

java 复制代码
//frameworks/base/services/core/java/com/android/server/wm/WindowOrientationListener.java
final class AccelSensorJudge extends OrientationJudge {

	 public void onSensorChanged(SensorEvent event) {

            float x = event.values[ACCELEROMETER_DATA_X];
            float y = event.values[ACCELEROMETER_DATA_Y];
            float z = event.values[ACCELEROMETER_DATA_Z];	  

			final long now = event.timestamp;
            final long then = mLastFilteredTimestampNanos;
            final float timeDeltaMS = (now - then) * 0.000001f;

			//各种计算函数

		    // Determine new proposed rotation.
            oldProposedRotation = mProposedRotation;
            if (mPredictedRotation < 0 || isPredictedRotationAcceptableLocked(now)) {
                mProposedRotation = mPredictedRotation;
            }
            proposedRotation = mProposedRotation;

			// Tell the listener.
            if (proposedRotation != oldProposedRotation && proposedRotation >= 0) {
            	//最终回调子类实现的onProposedRotationChanged函数
                onProposedRotationChanged(proposedRotation);
            }
	}

	@Override
        public void onAccuracyChanged(Sensor sensor, int accuracy) {
        }

		//判断当前的方向改变是否需要更新到系统
		private boolean isPredictedRotationAcceptableLocked(long now) {
            // The predicted rotation must have settled long enough.
            //当前角度需要保持40ms以上
            if (now < mPredictedRotationTimestampNanos + PROPOSAL_SETTLE_TIME_NANOS) {
                return false;
            }

            // The last flat state (time since picked up) must have been sufficiently long ago.
            //从手机平放着拿起需要500ms才会转屏
            if (now < mFlatTimestampNanos + PROPOSAL_MIN_TIME_SINCE_FLAT_ENDED_NANOS) {
                return false;
            }

            // The last swing state (time since last movement to put down) must have been sufficiently long ago.
            //晃动后300ms内都不能转屏
            if (now < mSwingTimestampNanos + PROPOSAL_MIN_TIME_SINCE_SWING_ENDED_NANOS) {
                return false;
            }

            // The last acceleration state must have been sufficiently long ago.
            //加速转动的时候500ms都不能转屏
            if (now < mAccelerationTimestampNanos
                    + PROPOSAL_MIN_TIME_SINCE_ACCELERATION_ENDED_NANOS) {
                return false;
            }

            // The last touch must have ended sufficiently long ago.
            //手没有触摸屏幕500ms才能转屏
            if (mTouched || now < mTouchEndedTimestampNanos
                    + PROPOSAL_MIN_TIME_SINCE_TOUCH_END_NANOS) {
                return false;
            }

            // Looks good!
            return true;
        }
}

可以看出以上二种实现,最终都会调用子类的onProposedRotationChanged函数来更新屏幕方向。

(C)监听SettingsProvider字段

另外在涉及到方向旋转功能上,DisplayRotation中还监听了以下四个SettingsProvider中的字段:

java 复制代码
//frameworks/base/services/core/java/com/android/server/wm/DisplayRotation.java

private class SettingsObserver extends ContentObserver {
        SettingsObserver(Handler handler) {
            super(handler);
        }

        void observe() {
            final ContentResolver resolver = mContext.getContentResolver();
            resolver.registerContentObserver(Settings.Secure.getUriFor(
                    Settings.Secure.SHOW_ROTATION_SUGGESTIONS), false, this,
                    UserHandle.USER_ALL);
            resolver.registerContentObserver(Settings.System.getUriFor(
                    Settings.System.ACCELEROMETER_ROTATION), false, this,
                    UserHandle.USER_ALL);
            resolver.registerContentObserver(Settings.System.getUriFor(
                    Settings.System.USER_ROTATION), false, this,
                    UserHandle.USER_ALL);
            resolver.registerContentObserver(
                    Settings.Secure.getUriFor(Settings.Secure.CAMERA_AUTOROTATE), false, this,
                    UserHandle.USER_ALL);

            updateSettings();
        }

        @Override
        public void onChange(boolean selfChange) {
            if (updateSettings()) {
                mService.updateRotation(true /* alwaysSendConfiguration */,
                        false /* forceRelayout */);
            }
        }
    }
  • Settings.System.ACCELEROMETER_ROTATION:该字段表示屏幕旋转模式,是否使用加速度传感器控制屏幕的方向旋转,开启时表示自由模式,关闭表示锁定模式;
  • Settings.System.USER_ROTATION:用户设置的屏幕旋转方向值,当没有使用加速度传感器,且顶层Activity没有指定旋转方向时作为默认值使用;
java 复制代码
//frameworks/base/services/core/java/com/android/server/wm/DisplayRotation.java
private boolean updateSettings() {
        final ContentResolver resolver = mContext.getContentResolver();
        // 是否更新旋转方向值
        boolean shouldUpdateRotation = false;

        synchronized (mLock) {
        	// 是否更新旋转方向监听
            boolean shouldUpdateOrientationListener = false;

            // Configure rotation suggestions.
            final int showRotationSuggestions =
                    ActivityManager.isLowRamDeviceStatic()
                            ? Settings.Secure.SHOW_ROTATION_SUGGESTIONS_DISABLED
                            : Settings.Secure.getIntForUser(resolver,
                            Settings.Secure.SHOW_ROTATION_SUGGESTIONS,
                            Settings.Secure.SHOW_ROTATION_SUGGESTIONS_DEFAULT,
                            UserHandle.USER_CURRENT);
            if (mShowRotationSuggestions != showRotationSuggestions) {
                mShowRotationSuggestions = showRotationSuggestions;
                shouldUpdateOrientationListener = true;
            }

            // Configure rotation lock.
            final int userRotation = Settings.System.getIntForUser(resolver,
                    Settings.System.USER_ROTATION, Surface.ROTATION_0,
                    UserHandle.USER_CURRENT);
            if (mUserRotation != userRotation) {
                mUserRotation = userRotation;
                shouldUpdateRotation = true;
            }

            final int userRotationMode = Settings.System.getIntForUser(resolver,
                    Settings.System.ACCELEROMETER_ROTATION, 0, UserHandle.USER_CURRENT) != 0
                            ? WindowManagerPolicy.USER_ROTATION_FREE
                            : WindowManagerPolicy.USER_ROTATION_LOCKED;
            if (mUserRotationMode != userRotationMode) {
                mUserRotationMode = userRotationMode;
                shouldUpdateOrientationListener = true;
                shouldUpdateRotation = true;
            }

			// 更新方向旋转监听状态
            if (shouldUpdateOrientationListener) {
                updateOrientationListenerLw(); // Enable or disable the orientation listener.
            }

            final int cameraRotationMode = Settings.Secure.getIntForUser(resolver,
                    Settings.Secure.CAMERA_AUTOROTATE, 0,
                    UserHandle.USER_CURRENT);
            if (mCameraRotationMode != cameraRotationMode) {
                mCameraRotationMode = cameraRotationMode;
                shouldUpdateRotation = true;
            }
        }

        return shouldUpdateRotation;
    }
  • shouldUpdateRotation = true表示需要更新旋转角度;
  • shouldUpdateOrientationListener = true表示要更新旋转方向的监听状态;
  • mUserRotationMode表示当前的方向旋转模式;

(D)方向旋转Sensor监听的注册与解除

旋转角度监听状态的更新在DisplayRotation.updateOrientationListenerLw()方法中,这里会进行旋转角度相关Sensor的注册和解除流程。

查看代码发现,在亮灭屏流程中,当keyguard绘制状态、window状态发生变化后,也都会通过DisplayRotation.updateOrientationListener()方法更新方向旋转Sensor的监听状态。

java 复制代码
public void updateOrientationListener() {
        synchronized (mLock) {
            updateOrientationListenerLw();
        }
    }

下面就来看一下,系统在什么场景下需要监听相关Sensor来进行方向旋转,什么情况下不需要监听。

java 复制代码
private void updateOrientationListenerLw() {
        if (mOrientationListener == null || !mOrientationListener.canDetectOrientation()) {
            // If sensor is turned off or nonexistent for some reason.
            return;
        }
		// 是否正在进行点亮屏幕的操作
        final boolean screenOnEarly = mDisplayPolicy.isScreenOnEarly();
        // 是否唤醒系统
        final boolean awake = mDisplayPolicy.isAwake();
        // keyguard绘制是否完成
        final boolean keyguardDrawComplete = mDisplayPolicy.isKeyguardDrawComplete();
        // 窗口绘制是否完成
        final boolean windowManagerDrawComplete = mDisplayPolicy.isWindowManagerDrawComplete();

        boolean disable = true;
        
        // 只有在屏幕唤醒状态,且keyguard和窗口全部绘制完成的情况下,才会有资格注册sensor监听
        if (screenOnEarly
                && (awake || mOrientationListener.shouldStayEnabledWhileDreaming())
                && ((keyguardDrawComplete && windowManagerDrawComplete))) {
            if (needSensorRunning()) {
                disable = false;
                // Enable listener if not already enabled.
                if (!mOrientationListener.mEnabled) {
                    mOrientationListener.enable();
                }
            }
        }
        // Check if sensors need to be disabled.
        if (disable) {
            mOrientationListener.disable();
        }
    }

对于旋转角度Sensor的注册/解除,会有多个因素决定,如是否亮屏、Keyguard绘制是否完成等。其规则是,在屏幕唤醒状态,且keyguard和窗口全部绘制完成的情况下,如果needSensorRunning()方法返回true,就会注册Sensor去监听方向旋转。

相关推荐
雪舞飞影6 个月前
Android屏幕旋转流程(1)
屏幕旋转·gsensor注册监听
seven27291 年前
android设置竖屏仍然跟随屏幕旋转怎么办
android·屏幕旋转