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

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

一、概述

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

转屏基于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      * <li>Screen turning off, should always disable listeners if already enabled.</li>
 928      * <li>Screen turned on and current app has sensor based orientation, enable listeners
 929      *     if not already enabled.</li>
 930      * <li>Screen turned on and current app does not have sensor orientation, disable listeners
 931      *     if already enabled.</li>
 932      * <li>Screen turning on and current app has sensor based orientation, enable listeners
 933      *     if needed.</li>
 934      * <li>screen turning on and current app has nosensor based orientation, do nothing.</li>
 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检查不做旋转

图: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.<init>(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--横竖屏旋转时背景色异常?

posted @ 2021-06-20 19:01  秋城  阅读(2991)  评论(2编辑  收藏  举报