Android11系统源码分析:屏幕旋转


Android11系统源码分析:屏幕旋转

目录
  • Android11系统源码分析:屏幕旋转
    • 一、概述
    • 二、情景分析:加速度传感器的注册监听
      • DisplayRotation#updateOrientationListenerLw
      • WindowOrientationListener#enable
      • SensorManager#registerListener
        • queue.addSensor
        • enableSensor
    • 三、情景分析:点击“自动旋转”
      • systemui进程
      • system_server进程
        • WindowManagerService.thawRotation
        • DisplayRotation.thawRotation
        • WindowManagerService.updateRotation
      • 总结一下
    • 四、情景分析:转屏流程
      • 3.1硬件、驱动参数上报
      • 3.2转屏算法确认是否转屏
      • 3.3 转屏动画
    • 五、参考

一、概述

本文转屏流程从自动旋转这一场景出发,研究设备横屏时系统框架的动作流程。

转屏基于Sensor框架,在system_server进程的开机打开屏幕阶段借助SensorManager注册加速度传感器的监听,以66.66ms的节奏接收回调结果。当传感器加速度数据回调时,会在WindowOrientationListener#onSensorChanged方法中算法处理是否需要旋转,主要受角度和加速度的影响。如果算法确定要旋转则通知到wms,在其updateRotationUnchecked方法中发起转屏动画和通知ATMS配置变动进而回调通知Activity。

本文关注流程,下一篇文章会聚焦耗时这一指标探讨“转屏”这一模块的性能优化。

整体架构较之前版本没有变化,流程有稍许变动。类关系如下(图转自Android中的转屏流程)

img

图:转屏相关类关系图

二、情景分析:加速度传感器的注册监听

Android系统Sensor传感器服务框架是另外一个课题,本文的转屏基于此。推荐以下两篇文章。

深入分析Android SensorService

Android Sensor Framework 概览


图:传感器注册及数据交互流程

上面两篇文章非常详细地剖析了sensor服务框架。

其中上图展示了上层(java)通过sdk接口:SensorManager.java#regitsterListener()监听具体传感器的调用流程。包括发起注册和注册后sensor数据传回来的流程。

转屏基于sensor框架,相对与sensor框架,转屏这个块更多可以理解为业务代码:system_server发起监听后收到sensor数据,之后才是wms中转屏的处理。所以sensor的架构流程本文不重点讨论。

下面简单过下system_server中转屏模块对加速度传感器的监听注册。

"android.ui@23798" prio=5 tid=0x14 nid=NA runnable
  java.lang.Thread.State: RUNNABLE
	 blocks Binder:490_1@24058
	 blocks Binder:490_2@24059
	 blocks android.display@24062
	 blocks android.anim@24063
	  at android.hardware.SystemSensorManager.registerListenerImpl(SystemSensorManager.java:184)
	  - locked <0x5d11> (a java.util.HashMap)
	  at android.hardware.SensorManager.registerListener(SensorManager.java:854)
	  at com.android.server.policy.WindowOrientationListener.enable(WindowOrientationListener.java:161)
	  - locked <0x5e61> (a java.lang.Object)
	  at com.android.server.wm.DisplayRotation$OrientationListener.enable(DisplayRotation.java:1498)
	  at com.android.server.wm.DisplayRotation.updateOrientationListenerLw(DisplayRotation.java:971)
	  at com.android.server.wm.DisplayRotation.updateOrientationListener(DisplayRotation.java:897)
	  - locked <0x5e60> (a com.android.server.wm.WindowManagerGlobalLock)
	  at com.android.server.policy.PhoneWindowManager.finishScreenTurningOn(PhoneWindowManager.java:4595)
	  at com.android.server.policy.PhoneWindowManager.finishWindowsDrawn(PhoneWindowManager.java:4589)
	  at com.android.server.policy.PhoneWindowManager.access$200(PhoneWindowManager.java:235)
	  at com.android.server.policy.PhoneWindowManager$PolicyHandler.handleMessage(PhoneWindowManager.java:661)
	  at android.os.Handler.dispatchMessage(Handler.java:106)
	  at android.os.Looper.loop(Looper.java:223)
	  at android.os.HandlerThread.run(HandlerThread.java:67)
	  at com.android.server.ServiceThread.run(ServiceThread.java:44)
	  at com.android.server.UiThread.run(UiThread.java:45)

如上是一份开机调用栈,本节的初始化流程指这个转屏传感器的监听注册。

开机流程中system_server进程默认注册传感器的监听。同样的在updateOrientationListenerLw方法的注释有详细说明,几种情况的监听动作。

需要理清的是,我们此处探讨system_server进程,而不是某个app使用了SensorManager来监听传感器。

所以当前的结论为,开机流程的开启屏幕阶段,wms会借助PhoneWindowManaegr在DisplayRotation中注册监听。如此传感器上报信息时可以回调通知(WindowOrientationListener.java$AccelSensorJudge#onSensorChanged),进而做算法处理是否转屏,做转屏动画,之后再通知atms做app的configChagne回调等。

下面就重点的代码展开分析。

DisplayRotation#updateOrientationListenerLw

frameworks/base/services/core/java/com/android/server/wm/DisplayRotation.java

 925     /**
 926      * Various use cases for invoking this function:
 927      * 
  • Screen turning off, should always disable listeners if already enabled.
  • 928 *
  • Screen turned on and current app has sensor based orientation, enable listeners 929 * if not already enabled.
  • 930 *
  • Screen turned on and current app does not have sensor orientation, disable listeners 931 * if already enabled.
  • 932 *
  • Screen turning on and current app has sensor based orientation, enable listeners 933 * if needed.
  • 934 *
  • screen turning on and current app has nosensor based orientation, do nothing.
  • 935 */ 936 private void updateOrientationListenerLw() { 937 if (mOrientationListener == null || !mOrientationListener.canDetectOrientation()) { 938 // If sensor is turned off or nonexistent for some reason. 939 return; 940 } 941 942 final boolean screenOnEarly = mDisplayPolicy.isScreenOnEarly(); 943 final boolean awake = mDisplayPolicy.isAwake(); 944 final boolean keyguardDrawComplete = mDisplayPolicy.isKeyguardDrawComplete(); 945 final boolean windowManagerDrawComplete = mDisplayPolicy.isWindowManagerDrawComplete(); 946 947 // Could have been invoked due to screen turning on or off or 948 // change of the currently visible window's orientation. 949 ProtoLog.v(WM_DEBUG_ORIENTATION, 950 "screenOnEarly=%b, awake=%b, currentAppOrientation=%d, " 951 + "orientationSensorEnabled=%b, keyguardDrawComplete=%b, " 952 + "windowManagerDrawComplete=%b", 953 screenOnEarly, awake, mCurrentAppOrientation, mOrientationListener.mEnabled, 954 keyguardDrawComplete, windowManagerDrawComplete); 955 956 boolean disable = true; 957 // Note: We postpone the rotating of the screen until the keyguard as well as the 958 // window manager have reported a draw complete or the keyguard is going away in dismiss 959 // mode. 960 if (screenOnEarly && awake && ((keyguardDrawComplete && windowManagerDrawComplete))) { 961 if (needSensorRunning()) { 962 disable = false; 963 // Enable listener if not already enabled. 964 if (!mOrientationListener.mEnabled) { 965 // Don't clear the current sensor orientation if the keyguard is going away in 966 // dismiss mode. This allows window manager to use the last sensor reading to 967 // determine the orientation vs. falling back to the last known orientation if 968 // the sensor reading was cleared which can cause it to relaunch the app that 969 // will show in the wrong orientation first before correcting leading to app 970 // launch delays. 971 mOrientationListener.enable(true /* clearCurrentRotation */); 972 } 973 } 974 } 975 // Check if sensors need to be disabled. 976 if (disable && mOrientationListener.mEnabled) { 977 mOrientationListener.disable(); 978 } 979 }

    方法头的注释是情况说明,什么时候会注册、关闭对转屏传感器的监听。

    重点关注960行的分支,在开机流程中,其值均为true。

    961行,needSensorRunning方法判断是否需要传感器开启,答案为默认开启,仅在关屏和用户主动关闭情况才会关掉。

    964行,开机时mEnabled为初值false

    走到971行,enable方法里开始注册。需要注意的是,OrientationListener是WindowOrientationListener的子类,enable的实现在父类中,如下。

    WindowOrientationListener#enable

    frameworks/base/services/core/java/com/android/server/wm/DisplayRotation.java

    private class OrientationListener extends WindowOrientationListener {
            @Override
            public void enable(boolean clearCurrentRotation) {
                super.enable(clearCurrentRotation);
                mEnabled = true;
                ProtoLog.v(WM_DEBUG_ORIENTATION, "Enabling listeners");
            }
    

    frameworks/base/services/core/java/com/android/server/policy/WindowOrientationListener.java

     139     /**
     140      * Enables the WindowOrientationListener so it will monitor the sensor and call
     141      * {@link #onProposedRotationChanged(int)} when the device orientation changes.
     142      *
     143      * @param clearCurrentRotation True if the current proposed sensor rotation should be cleared as
     144      *                             part of the reset.
     145      */
     146     public void enable(boolean clearCurrentRotation) {
     147         synchronized (mLock) {
     148             if (mSensor == null) {
     149                 Slog.w(TAG, "Cannot detect sensors. Not enabled");
     150                 return;
     151             }
     152             if (mEnabled) {
     153                 return;
     154             }
     155             if (LOG) {
     156                 Slog.d(TAG, "WindowOrientationListener enabled clearCurrentRotation="
     157                         + clearCurrentRotation);
     158             }
     159             mOrientationJudge.resetLocked(clearCurrentRotation);
     160             if (mSensor.getType() == Sensor.TYPE_ACCELEROMETER) {
     161                 mSensorManager.registerListener(
     162                         mOrientationJudge, mSensor, mRate, DEFAULT_BATCH_LATENCY, mHandler);
     163             } else {
     164                 mSensorManager.registerListener(mOrientationJudge, mSensor, mRate, mHandler);
     165             }
     166             mEnabled = true;
     167         }
     168     }
    

    注释说的很清楚,enable方法注册传感器监听,当传感器数据上报时回调方法#onProposedRotationChanged。

    走进161行分支,看下几个关键的参数。

    在WindowOrientationListener的构造函数中对mSensor和mOrientationJudge完成了初始化。

        public WindowOrientationListener(Context context, Handler handler) {
            this(context, handler, SensorManager.SENSOR_DELAY_UI);
        }
        private WindowOrientationListener(Context context, Handler handler, int rate) {
            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);
    
    • mSensor是TYPE_ACCELEROMETER加速度传感器

    模拟器上的传感器参数是这样:{Sensor name="Goldfish 3-axis Accelerometer", vendor="The Android Open Source Project", version=1, type=1, maxRange=39.300007, resolution=2.480159E-4, power=3.0, minDelay=10000}

    • mOrientationJudge是AccelSensorJudge类的实例

      这个类很重要,转屏算法就在他的onSensorChanged方法中。算法内容可以参考这篇博文:Android转屏流程与优化(Google转屏算法)

    • mRate

      代表延迟水平,有这几种定义。此处默认设为2,接收到的回调时间间隔66.666666ms。

      frameworks/base/core/java/android/hardware/SensorManager.java

       317     /** get sensor data as fast as possible */
       318     public static final int SENSOR_DELAY_FASTEST = 0;
       319     /** rate suitable for games */
       320     public static final int SENSOR_DELAY_GAME = 1;
       321     /** rate suitable for the user interface  */
       322     public static final int SENSOR_DELAY_UI = 2;
       323     /** rate (default) suitable for screen orientation changes */
       324     public static final int SENSOR_DELAY_NORMAL = 3;
      
      
    • DEFAULT_BATCH_LATENCY

      最大批量报告延迟,us微秒

    • handler

      是我们的UiThread的handler,我们当前是处于system_server进程的ui线程中。堆栈信息有写。

    最后阶段,看下registerListener的实现

    SensorManager#registerListener

    frameworks/base/core/java/android/hardware/SensorManager.java

     851     public boolean registerListener(SensorEventListener listener, Sensor sensor,
     852             int samplingPeriodUs, int maxReportLatencyUs, Handler handler) {
     853         int delayUs = getDelay(samplingPeriodUs);
     854         return registerListenerImpl(listener, sensor, delayUs, handler, maxReportLatencyUs, 0);
     855     }
    

    frameworks/base/core/java/android/hardware/SystemSensorManager.java

    144     @Override
    145     protected boolean registerListenerImpl(SensorEventListener listener, Sensor sensor,
    146             int delayUs, Handler handler, int maxBatchReportLatencyUs, int reservedFlags) {
    147         if (listener == null || sensor == null) {
    148             Log.e(TAG, "sensor or listener is null");
    149             return false;
    150         }
    151         // Trigger Sensors should use the requestTriggerSensor call.
    152         if (sensor.getReportingMode() == Sensor.REPORTING_MODE_ONE_SHOT) {
    153             Log.e(TAG, "Trigger Sensors should use the requestTriggerSensor.");
    154             return false;
    155         }
    156         if (maxBatchReportLatencyUs < 0 || delayUs < 0) {
    157             Log.e(TAG, "maxBatchReportLatencyUs and delayUs should be non-negative");
    158             return false;
    159         }
    160         if (mSensorListeners.size() >= MAX_LISTENER_COUNT) {
    161             throw new IllegalStateException("register failed, "
    162                 + "the sensor listeners size has exceeded the maximum limit "
    163                 + MAX_LISTENER_COUNT);
    164         }
    165 
    166         // Invariants to preserve:
    167         // - one Looper per SensorEventListener
    168         // - one Looper per SensorEventQueue
    169         // We map SensorEventListener to a SensorEventQueue, which holds the looper
    170         synchronized (mSensorListeners) {
    171             SensorEventQueue queue = mSensorListeners.get(listener);
    172             if (queue == null) {
    173                 Looper looper = (handler != null) ? handler.getLooper() : mMainLooper;
    174                 final String fullClassName =
    175                         listener.getClass().getEnclosingClass() != null
    176                             ? listener.getClass().getEnclosingClass().getName()
    177                             : listener.getClass().getName();
    178                 queue = new SensorEventQueue(listener, looper, this, fullClassName);
    179                 if (!queue.addSensor(sensor, delayUs, maxBatchReportLatencyUs)) {
    180                     queue.dispose();
    181                     return false;
    182                 }
    183                 mSensorListeners.put(listener, queue);
    184                 return true;
    185             } else {
    186                 return queue.addSensor(sensor, delayUs, maxBatchReportLatencyUs);
    187             }
    188         }
    189     }
    

    两个重点,178行SensorEventQueue初始化,179行addSensor方法,都主要在native里。

    queue.addSensor

    上面小节,走过漫长的queue初始化,下面开始使用这个初始化后的队列,增加传感器监听。

    frameworks/base/core/java/android/hardware/SystemSensorManager.java

    629     private abstract static class BaseEventQueue {
    663         public boolean addSensor(
    664                 Sensor sensor, int delayUs, int maxBatchReportLatencyUs) {
    665             // Check if already present.
    666             int handle = sensor.getHandle();
    667             if (mActiveSensors.get(handle)) return false;
    668 
    669             // Get ready to receive events before calling enable.
    670             mActiveSensors.put(handle, true);
    671             addSensorEvent(sensor);
    672             if (enableSensor(sensor, delayUs, maxBatchReportLatencyUs) != 0) {
    673                 // Try continuous mode if batching fails.
    674                 if (maxBatchReportLatencyUs == 0
    675                         || maxBatchReportLatencyUs > 0 && enableSensor(sensor, delayUs, 0) != 0) {
    676                     removeSensor(sensor, false);
    677                     return false;
    678                 }
    679             }
    680             return true;
    681         }
    

    670行,mActiveSensors是个缓存。

    671行addSensorEvent实现在子类SensorEventQueue中。

    frameworks/base/core/java/android/hardware/SystemSensorManager.java

        static final class SensorEventQueue extends BaseEventQueue {
            @Override
            public void addSensorEvent(Sensor sensor) {
                SensorEvent t = new SensorEvent(Sensor.getMaxLengthValuesArray(sensor,
                        mManager.mTargetSdkLevel));
                synchronized (mSensorsEvents) {
                    mSensorsEvents.put(sensor.getHandle(), t);
    

    这里的mSensorsEvents也是个缓存array。

    enableSensor

    frameworks/base/core/java/android/hardware/SystemSensorManager.java

        static final class SensorEventQueue extends BaseEventQueue {
    		private int enableSensor(
                    Sensor sensor, int rateUs, int maxBatchReportLatencyUs) {
                if (mNativeSensorEventQueue == 0) throw new NullPointerException();
                if (sensor == null) throw new NullPointerException();
                return nativeEnableSensor(mNativeSensorEventQueue, sensor.getHandle(), rateUs,
                        maxBatchReportLatencyUs);
    

    native的代码都略去了,详细可以阅读上面的两篇推荐文。

    system_server进程中开机流程的wms屏幕开启阶段,加速度传感器的监听注册流程大致如此。

    三、情景分析:点击“自动旋转”

    设备中自动旋转的开关在systemui下拉菜单中,如下图

    systemui进程

    第一份binder trace如下

    Trace: java.lang.Throwable
    	at android.os.BinderProxy.transact(BinderProxy.java:509)
    	at android.content.ContentProviderProxy.call(ContentProviderNative.java:730)
    	at android.provider.Settings$NameValueCache.putStringForUser(Settings.java:2667)
    	at android.provider.Settings$System.putStringForUser(Settings.java:3258)
    	at android.provider.Settings$System.putStringForUser(Settings.java:3242)
    	at android.provider.Settings$System.putIntForUser(Settings.java:3367)
    	at com.android.internal.view.RotationPolicy.setRotationLockAtAngle(RotationPolicy.java:122)
    	at com.android.internal.view.RotationPolicy.setRotationLock(RotationPolicy.java:114)
    	at com.android.systemui.statusbar.policy.RotationLockControllerImpl.setRotationLocked(RotationLockControllerImpl.java:68)
    	at com.android.systemui.qs.tiles.RotationLockTile.handleClick(RotationLockTile.java:62)
    	at com.android.systemui.qs.tileimpl.QSTileImpl$H.handleMessage(QSTileImpl.java:561)
    	at android.os.Handler.dispatchMessage(Handler.java:106)
    	at android.os.Looper.loop(Looper.java:223)
    	at android.os.HandlerThread.run(HandlerThread.java:67)
    

    可以看到,自动旋转暴露给app层的接口为com.android.internal.view.RotationPolicy.setRotationLock

    frameworks/base/core/java/com/android/internal/view/RotationPolicy.java

    109     /**
    110      * Enables or disables rotation lock from the system UI toggle.
    111      */
    112     public static void setRotationLock(Context context, final boolean enabled) {
    113         final int rotation = areAllRotationsAllowed(context) ? CURRENT_ROTATION : NATURAL_ROTATION;
    114         setRotationLockAtAngle(context, enabled, rotation);
    115     }
    116 
    117     /**
    118      * Enables or disables rotation lock at a specific rotation from system UI.
    119      */
    120     public static void setRotationLockAtAngle(Context context, final boolean enabled,
    121             final int rotation) {
    122         Settings.System.putIntForUser(context.getContentResolver(),
    123                 Settings.System.HIDE_ROTATION_LOCK_TOGGLE_FOR_ACCESSIBILITY, 0,
    124                 UserHandle.USER_CURRENT);
    125 
    126         setRotationLock(enabled, rotation);
    127     }
    

    跟到122行,修改的数据库字段为Settings.System.HIDE_ROTATION_LOCK_TOGGLE_FOR_ACCESSIBILITY和辅助服务有关,0代表不隐藏。此处不重点关注。

    126行,在这个方法中使用AsyncTask向wms发起binder ipc

    frameworks/base/core/java/com/android/internal/view/RotationPolicy.java

    146     private static void setRotationLock(final boolean enabled, final int rotation) {
    147         AsyncTask.execute(new Runnable() {
    148             @Override
    149             public void run() {
    150                 try {
    151                     IWindowManager wm = WindowManagerGlobal.getWindowManagerService();
    152                     if (enabled) {
    153                         wm.freezeRotation(rotation);
    154                     } else {
    155                         wm.thawRotation();
    156                     }
    157                 } catch (RemoteException exc) {
    158                     Log.w(TAG, "Unable to save auto-rotate setting");
    159                 }
    160             }
    161         });
    162     }
    

    同样的在trace中体现如下

    Trace: java.lang.Throwable
    	at android.os.BinderProxy.transact(BinderProxy.java:509)
    	at android.view.IWindowManager$Stub$Proxy.thawRotation(IWindowManager.java:3902)
    	at com.android.internal.view.RotationPolicy$1.run(RotationPolicy.java:155)
    	at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:305)
    	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
    	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
    	at java.lang.Thread.run(Thread.java:923)
    

    接下来进入system_server进程,看下wms的处理

    system_server进程

    binder trace如下

    Trace: java.lang.Throwable
    	at android.os.BinderProxy.transact(BinderProxy.java:509)
    	at com.android.internal.statusbar.IStatusBar$Stub$Proxy.setTopAppHidesStatusBar(IStatusBar.java:1652)
    	at com.android.server.statusbar.StatusBarManagerService$1.setTopAppHidesStatusBar(StatusBarManagerService.java:423)
    	at com.android.server.wm.StatusBarController.setTopAppHidesStatusBar(StatusBarController.java:102)
    	at com.android.server.wm.DisplayPolicy.finishPostLayoutPolicyLw(DisplayPolicy.java:2748)
    	at com.android.server.wm.DisplayContent.applySurfaceChangesTransaction(DisplayContent.java:3940)
    	at com.android.server.wm.RootWindowContainer.applySurfaceChangesTransaction(RootWindowContainer.java:1068)
    	at com.android.server.wm.RootWindowContainer.performSurfacePlacementNoTrace(RootWindowContainer.java:845)
    	at com.android.server.wm.RootWindowContainer.performSurfacePlacement(RootWindowContainer.java:802)
    	at com.android.server.wm.WindowSurfacePlacer.performSurfacePlacementLoop(WindowSurfacePlacer.java:178)
    	at com.android.server.wm.WindowSurfacePlacer.performSurfacePlacement(WindowSurfacePlacer.java:127)
    	at com.android.server.wm.WindowSurfacePlacer.performSurfacePlacement(WindowSurfacePlacer.java:116)
    	at com.android.server.wm.WindowManagerService.updateRotationUnchecked(WindowManagerService.java:3844)
    	at com.android.server.wm.WindowManagerService.thawDisplayRotation(WindowManagerService.java:3776)
    	at com.android.server.wm.WindowManagerService.thawRotation(WindowManagerService.java:3746)
    	at android.view.IWindowManager$Stub.onTransact(IWindowManager.java:1948)
    	at com.android.server.wm.WindowManagerService.onTransact(WindowManagerService.java:1350)
    	at android.os.Binder.execTransactInternal(Binder.java:1154)
    	at android.os.Binder.execTransact(Binder.java:1123)
    

    binder trace、有时候并不能如你的意,因为只有ipc才会被收集调用栈,oneway的情况就只可以看到发起进程的栈了。另外,服务端进程的栈有些进程内部的动作,也无法体现在trace上。例如上面这份trace,显示wms里bidner ipc沟通statusbar,这个动作不是我们主要关注的内容,wms内部的处理流程才是。

    所以binder trace不一定完全切合我们需求,具体情景具体分析。下面我们跟踪这份trace看wms内部的处理。

    WindowManagerService.thawRotation

    frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java

    3754     @Override
    3755     public void thawRotation() {
    3756         thawDisplayRotation(Display.DEFAULT_DISPLAY);
    3757     }
    3763     @Override
    3764     public void thawDisplayRotation(int displayId) {
    3765         if (!checkCallingPermission(android.Manifest.permission.SET_ORIENTATION,
    3766                 "thawRotation()")) {
    3767             throw new SecurityException("Requires SET_ORIENTATION permission");
    3768         }
    3769 
    3770         ProtoLog.v(WM_DEBUG_ORIENTATION, "thawRotation: mRotation=%d", getDefaultDisplayRotation());
    3771 
    3772         long origId = Binder.clearCallingIdentity();
    3773         try {
    3774             synchronized (mGlobalLock) {
    3775                 final DisplayContent display = mRoot.getDisplayContent(displayId);
    3776                 if (display == null) {
    3777                     Slog.w(TAG, "Trying to thaw rotation for a missing display.");
    3778                     return;
    3779                 }
    3780                 display.getDisplayRotation().thawRotation();
    3781             }
    3782         } finally {
    3783             Binder.restoreCallingIdentity(origId);
    3784         }
    3785 
    3786         updateRotationUnchecked(false, false);
    3787     }
    

    3765行鉴权,这是系统api。

    3780行,在DisplayRotation中设置settings lib 数据库。

    3786行,updateRotationUnchecked方法是重点。

    DisplayRotation.thawRotation

    frameworks/base/services/core/java/com/android/server/wm/DisplayRotation.java

     826     void thawRotation() {
     827         setUserRotation(WindowManagerPolicy.USER_ROTATION_FREE, mUserRotation);
     828     }
      790     @VisibleForTesting
     791     void setUserRotation(int userRotationMode, int userRotation) {
     792         if (isDefaultDisplay) {
     793             // We'll be notified via settings listener, so we don't need to update internal values.
     794             final ContentResolver res = mContext.getContentResolver();
     795             final int accelerometerRotation =
     796                     userRotationMode == WindowManagerPolicy.USER_ROTATION_LOCKED ? 0 : 1;
     797             Settings.System.putIntForUser(res, Settings.System.ACCELEROMETER_ROTATION,
     798                     accelerometerRotation, UserHandle.USER_CURRENT);
     799             Settings.System.putIntForUser(res, Settings.System.USER_ROTATION, userRotation,
     800                     UserHandle.USER_CURRENT);
     801             return;
     802         }
     803 
     804         boolean changed = false;
     805         if (mUserRotationMode != userRotationMode) {
     806             mUserRotationMode = userRotationMode;
     807             changed = true;
     808         }
     809         if (mUserRotation != userRotation) {
     810             mUserRotation = userRotation;
     811             changed = true;
     812         }
     813         mDisplayWindowSettings.setUserRotation(mDisplayContent, userRotationMode,
     814                 userRotation);
     815         if (changed) {
     816             mService.updateRotation(true /* alwaysSendConfiguration */,
     817                     false /* forceRelayout */);
     818         }
     819     }   
    

    797、799行更新两个重要的数据库值

    Settings.System.ACCELEROMETER_ROTATION

    Settings.System.USER_ROTATION

    设置之后监听这些值的地方会触发ContentResolver回调,例如进程外的systemui、launcher和进程内DisplayRotation。

    我们关注系统进程的数据库监听。

    frameworks/base/services/core/java/com/android/server/wm/DisplayRotation.java

    1511     private class SettingsObserver extends ContentObserver {
    1512         SettingsObserver(Handler handler) {
    1513             super(handler);
    1514         }
    1515 
    1516         void observe() {
    1517             final ContentResolver resolver = mContext.getContentResolver();
    1518             resolver.registerContentObserver(Settings.Secure.getUriFor(
    1519                     Settings.Secure.SHOW_ROTATION_SUGGESTIONS), false, this,
    1520                     UserHandle.USER_ALL);
    1521             resolver.registerContentObserver(Settings.System.getUriFor(
    1522                     Settings.System.ACCELEROMETER_ROTATION), false, this,
    1523                     UserHandle.USER_ALL);
    1524             resolver.registerContentObserver(Settings.System.getUriFor(
    1525                     Settings.System.USER_ROTATION), false, this,                                                                   
    1526                     UserHandle.USER_ALL);
    1527             updateSettings();
    1528         }
    1529 
    1530         @Override
    1531         public void onChange(boolean selfChange) {
    1532             if (updateSettings()) {
    1533                 mService.updateRotation(true /* alwaysSendConfiguration */,
    1534                         false /* forceRelayout */);
    1535             }
    1536         }
    1537     }
    

    1531行的回调得到触发。1532行默认为true,代表需要wms做进一步处理。

    走到1533行,wms的updateRotation方法。参数1代表要更新配置,参数2为不强制更新布局。

    WindowManagerService.updateRotation

    frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java

    3806     /**
    3807      * Recalculate the current rotation.
    3808      *
    3809      * Called by the window manager policy whenever the state of the system changes
    3810      * such that the current rotation might need to be updated, such as when the
    3811      * device is docked or rotated into a new posture.
    3812      */
    3813     @Override
    3814     public void updateRotation(boolean alwaysSendConfiguration, boolean forceRelayout) {
    3815         updateRotationUnchecked(alwaysSendConfiguration, forceRelayout);
    3816     }
    
    3818     private void updateRotationUnchecked(boolean alwaysSendConfiguration, boolean forceRelayout) {
    3819         ProtoLog.v(WM_DEBUG_ORIENTATION, "updateRotationUnchecked:"
    3820                         + " alwaysSendConfiguration=%b forceRelayout=%b",
    3821                 alwaysSendConfiguration, forceRelayout);
    3822 
    3823         Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "updateRotation");
    3824 
    3825         long origId = Binder.clearCallingIdentity();
    3826 
    3827         try {
    3828             synchronized (mGlobalLock) {
    3829                 boolean layoutNeeded = false;
    3830                 final int displayCount = mRoot.mChildren.size();
    3831                 for (int i = 0; i < displayCount; ++i) {
    3832                     final DisplayContent displayContent = mRoot.mChildren.get(i);
    3833                     Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "updateRotation: display");
    3834                     final boolean rotationChanged = displayContent.updateRotationUnchecked();
    3835                     Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
    3836 
    3837                     if (rotationChanged) {
    3838                         mAtmService.getTaskChangeNotificationController()
    3839                                 .notifyOnActivityRotation(displayContent.mDisplayId);
    3840                     }
    3841 
    3842                     if (!rotationChanged || forceRelayout) {
    3843                         displayContent.setLayoutNeeded();
    3844                         layoutNeeded = true;
    3845                     }
    3846                     if (rotationChanged || alwaysSendConfiguration) {
    3847                         displayContent.sendNewConfiguration();
    3848                     }
    3849                 }
    3850 
    3851                 if (layoutNeeded) {
    3852                     Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER,
    3853                             "updateRotation: performSurfacePlacement");
    3854                     mWindowPlacerLocked.performSurfacePlacement();
    3855                     Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
    3856                 }
    3857             }
    3858         } finally {
    3859             Binder.restoreCallingIdentity(origId);
    3860             Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
    3861         }
    3862     }
    

    3834行displayContent.updateRotationUnchecked()计算是否需要旋转。由于这次我们的点击操作是在屏幕竖起时,所以计算不做旋转,如下图。那另一种情况,横屏时点击的话,自然就触发旋转了。这也符合我们的认知。

    图:displayContent.updateRotationUnchecked检查不做旋转

    3837行,rotationChanged为false,所以不需要更新配置。

    3842、3846、3851行本次流程没什么需要关注的。

    总结一下

    • 点击自动旋转涉及两个进程三个角色。

    两个进程:systemui、system_server

    三个角色:systemui界面按钮、settingslib数据库、DisplayRotation

    • 动作流程

    人点击systemui的自动旋转按钮,binder沟通wm.thawRotation

    wms写settingslib:Settings.System.ACCELEROMETER_ROTATION、Settings.System.USER_ROTATION

    DisplayRotation监听这俩数据库值,触发转屏检查mService.updateRotation

    四、情景分析:转屏流程

    转屏从人的操作体验上来说,可以这样描述:把屏幕横过来,经过短暂的等待看到屏幕上图像开始旋转,然后是图像旋转结束。

    从代码流程上看,可以分为三部分

    一是硬件、驱动检测到加速度变化然后传给监听者(WindowOrientationListener)

    二是WindowOrientationListener的转屏算法,确定是否要转

    三是wms里做转屏动画

    3.1硬件、驱动参数上报

    本文不讨论这块

    3.2转屏算法确认是否转屏

    见性能优化篇。

    3.3 转屏动画

    "android.ui@23942" prio=5 tid=0x14 nid=NA runnable
      java.lang.Thread.State: RUNNABLE
    	  at com.android.server.wm.DisplayRotation$OrientationListener.onProposedRotationChanged(DisplayRotation.java:1487)
    	  at com.android.server.policy.WindowOrientationListener$AccelSensorJudge.onSensorChanged(WindowOrientationListener.java:818)
    	  at android.hardware.SystemSensorManager$SensorEventQueue.dispatchSensorEvent(SystemSensorManager.java:837)
    	  at android.os.MessageQueue.nativePollOnce(MessageQueue.java:-1)
    	  at android.os.MessageQueue.next(MessageQueue.java:335)
    	  at android.os.Looper.loop(Looper.java:183)
    	  at android.os.HandlerThread.run(HandlerThread.java:67)
    	  at com.android.server.ServiceThread.run(ServiceThread.java:44)
    	  at com.android.server.UiThread.run(UiThread.java:45)
    

    这是一份堆栈信息,展示了转屏流程的第一阶段,是在system_server进程android.ui线程。

    回调是looper的epoll对fd的监听,详细可以看第一节的两篇文章和学习下looper的监听机制。

    转屏算法处理在WindowOrientationListener$AccelSensorJudge.onSensorChanged回调方法中,本文不展开。

    走出了onSensorChanged方法即代表算法认可当前需要旋转,所以下一步是wms做处理动作,我们继续往下看。

    frameworks/base/services/core/java/com/android/server/wm/DisplayRotation.java

    @Override
    public void onProposedRotationChanged(int rotation) {
        ProtoLog.v(WM_DEBUG_ORIENTATION, "onProposedRotationChanged, rotation=%d", rotation);
        Runnable r = mRunnableCache.get(rotation, null);
        if (r == null) {
            r = new UpdateRunnable(rotation);
            mRunnableCache.put(rotation, r);
        }
        getHandler().post(r);
    }
    

    执行runnable

    frameworks/base/services/core/java/com/android/server/wm/DisplayRotation.java

    private class UpdateRunnable implements Runnable {
        final int mRotation;
        UpdateRunnable(int rotation) {
            mRotation = rotation;
        }
        @Override
        public void run() {
            // Send interaction hint to improve redraw performance.
            mService.mPowerManagerInternal.powerHint(PowerHint.INTERACTION, 0);
            if (isRotationChoicePossible(mCurrentAppOrientation)) {
                final boolean isValid = isValidRotationChoice(mRotation);
                sendProposedRotationChangeToStatusBarInternal(mRotation, isValid);
            } else {
                mService.updateRotation(false /* alwaysSendConfiguration */,
                        false /* forceRelayout */);
    

    if判断为false,走到mService.updateRotation。这个是wms暴露的转屏接口,上一节的自动转屏也是走到这个API。

    先放一下调用栈

    "android.ui@23972" prio=5 tid=0x14 nid=NA runnable
      java.lang.Thread.State: RUNNABLE
    	 blocks android.fg@24280
    	 blocks android.display@24282
    	 blocks android.anim@24283
    	 blocks ActivityManager@24287
    	  at com.android.server.wm.ScreenRotationAnimation.setRotationTransform(ScreenRotationAnimation.java:270)
    	  at com.android.server.wm.ScreenRotationAnimation.setRotation(ScreenRotationAnimation.java:332)
    	  at com.android.server.wm.ScreenRotationAnimation.(ScreenRotationAnimation.java:254)
    	  at com.android.server.wm.WindowManagerService.startFreezingDisplay(WindowManagerService.java:5610)
    	  at com.android.server.wm.WindowManagerService.startFreezingDisplay(WindowManagerService.java:5562)
    	  at com.android.server.wm.DisplayRotation.prepareNormalRotationAnimation(DisplayRotation.java:549)
    	  at com.android.server.wm.DisplayRotation.updateRotationUnchecked(DisplayRotation.java:491)
    	  at com.android.server.wm.DisplayContent.updateRotationUnchecked(DisplayContent.java:1700)
    	  at com.android.server.wm.WindowManagerService.updateRotationUnchecked(WindowManagerService.java:3834)
    	  - locked <0x5f48> (a com.android.server.wm.WindowManagerGlobalLock)
    	  at com.android.server.wm.WindowManagerService.updateRotation(WindowManagerService.java:3815)
    	  at com.android.server.wm.DisplayRotation$OrientationListener$UpdateRunnable.run(DisplayRotation.java:1479)
    	  at android.os.Handler.handleCallback(Handler.java:938)
    	  at android.os.Handler.dispatchMessage(Handler.java:99)
    	  at android.os.Looper.loop(Looper.java:223)
    	  at android.os.HandlerThread.run(HandlerThread.java:67)
    	  at com.android.server.ServiceThread.run(ServiceThread.java:44)
    	  at com.android.server.UiThread.run(UiThread.java:45)
    

    这是冻结屏幕准备参数的的调用栈

    "android.display@23994" prio=5 tid=0x16 nid=NA runnable
      java.lang.Thread.State: RUNNABLE
    	 blocks android.anim@23993
    	 blocks Binder:486_D@24124
    	 blocks Binder:486_E@23997
    	  at com.android.server.wm.ScreenRotationAnimation$SurfaceRotationAnimationController.startScreenRotationAnimation(ScreenRotationAnimation.java:576)
    	  at com.android.server.wm.ScreenRotationAnimation.startAnimation(ScreenRotationAnimation.java:427)
    	  at com.android.server.wm.ScreenRotationAnimation.dismiss(ScreenRotationAnimation.java:445)
    	  at com.android.server.wm.WindowManagerService.stopFreezingDisplayLocked(WindowManagerService.java:5682)
    	  at com.android.server.wm.ActivityRecord.stopFreezingScreen(ActivityRecord.java:5311)
    	  at com.android.server.wm.ActivityRecord.onAppFreezeTimeout(ActivityRecord.java:5272)
    	  at com.android.server.wm.WindowManagerService$H.handleMessage(WindowManagerService.java:4932)
    	  - locked <0x5e42> (a com.android.server.wm.WindowManagerGlobalLock)
    	  at android.os.Handler.dispatchMessage(Handler.java:106)
    	  at android.os.Looper.loop(Looper.java:223)
    	  at android.os.HandlerThread.run(HandlerThread.java:67)
    	  at com.android.server.ServiceThread.run(ServiceThread.java:44)
    

    这是过渡动画启动的调用栈

    动画的启动流程此处不展开,细节可参考:Android 11--横竖屏旋转时背景色异常?

    需要关注的主要有两处,一是动画,二是通知ATMS做config的变动进而通知activity响应转屏,都在wms的updateRotationUnchecked方法里

    frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java

    3818     private void updateRotationUnchecked(boolean alwaysSendConfiguration, boolean forceRelayout) {
    3819         ProtoLog.v(WM_DEBUG_ORIENTATION, "updateRotationUnchecked:"
    3820                         + " alwaysSendConfiguration=%b forceRelayout=%b",
    3821                 alwaysSendConfiguration, forceRelayout);
    3822 
    3823         Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "updateRotation");
    3824 
    3825         long origId = Binder.clearCallingIdentity();
    3826 
    3827         try {
    3828             synchronized (mGlobalLock) {
    3829                 boolean layoutNeeded = false;
    3830                 final int displayCount = mRoot.mChildren.size();
    3831                 for (int i = 0; i < displayCount; ++i) {
    3832                     final DisplayContent displayContent = mRoot.mChildren.get(i);
    3833                     Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "updateRotation: display");
    3834                     final boolean rotationChanged = displayContent.updateRotationUnchecked();
    3835                     Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
    3836 
    3837                     if (rotationChanged) {
    3838                         mAtmService.getTaskChangeNotificationController()
    3839                                 .notifyOnActivityRotation(displayContent.mDisplayId);
    3840                     }
    3841 
    3842                     if (!rotationChanged || forceRelayout) {
    3843                         displayContent.setLayoutNeeded();
    3844                         layoutNeeded = true;
    3845                     }
    3846                     if (rotationChanged || alwaysSendConfiguration) {
    3847                         displayContent.sendNewConfiguration();
    3848                     }
    3849                 }
    3850 
    3851                 if (layoutNeeded) {
    3852                     Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER,
    3853                             "updateRotation: performSurfacePlacement");
    3854                     mWindowPlacerLocked.performSurfacePlacement();
    3855                     Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
    3856                 }
    3857             }
    3858         } finally {
    3859             Binder.restoreCallingIdentity(origId);
    3860             Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
    3861         }
    3862     }                                  
    

    重点1:3834行处理转屏动画

    重点2:3838,3847行通知ATMS处理旋转和配置变动

    五、参考

    深入分析Android SensorService

    Android Sensor Framework 概览

    Android中的转屏流程

    Android 11--横竖屏旋转时背景色异常?