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--橫豎屏旋轉時背景色異常?


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM