Android支持横屏和竖屏,用户可以选择锁定(rotation lock)也可以选择让传感器来自动转屏。而转屏时为了使用户体验更流畅,会对屏幕截屏,然后使用截屏的图来做转屏动画,直到转屏动作结束。
参考:https://blog.csdn.net/jinzhuojun/article/details/50085491 参考:https://blog.csdn.net/kc58236582/article/details/53741445
调试方法 打开debug log开关 frameworks/base/services/core/java/com/android/server/wm/WindowManagerDebugConfig.java 1 2 3 static final String TAG_WM = "WindowManager" ;static final boolean DEBUG_ORIENTATION = false ; static final boolean DEBUG_APP_ORIENTATION = false ;
adb logcat -v threadtime|grep -Ei "rotation|ActivityTaskManager|WindowOrientationListener"
Settings设置开启/关闭自动旋转屏幕 是否要自动转屏是在Setting中设置的。为了监听Setting中的改动,系统启动时,PhoneWindowManager
的init()函数中创建了SettingsObserver对象。
它的observe()方法会监听Settings.System.USER_ROTATION
的值(Android Q中此处没有这个property了)。
1 2 3 4 5 6 public void init (Context context, IWindowManager windowManager, WindowManagerFuncs windowManagerFuncs) { ... mSettingsObserver = new SettingsObserver(mHandler); mSettingsObserver.observe(); ...
当用户在Setting中设置自动转屏后,会触发以下流程:
public boolean onPreferenceTreeClick(Preference preference)
:packages/apps/Settings/src/com/android/settings/accessibility/AccessibilitySettings.java
handleLockScreenRotationPreferenceClick()
:被调用
setRotationLockForAccessibility(Context context, final boolean enabled)
frameworks/base/core/java/com/android/internal/view/RotationPolicy.java 1 2 3 4 5 6 7 public static void setRotationLockForAccessibility (Context context, final boolean enabled) { Settings.System.putIntForUser(context.getContentResolver(), Settings.System.HIDE_ROTATION_LOCK_TOGGLE_FOR_ACCESSIBILITY, enabled ? 1 : 0 , UserHandle.USER_CURRENT); setRotationLock(enabled, NATURAL_ROTATION); }
setRotationLockForAccessibility
frameworks/base/core/java/com/android/internal/view/RotationPolicy.java 1 2 3 4 5 6 7 public static void setRotationLockForAccessibility (Context context, final boolean enabled) { Settings.System.putIntForUser(context.getContentResolver(), Settings.System.HIDE_ROTATION_LOCK_TOGGLE_FOR_ACCESSIBILITY, enabled ? 1 : 0 , UserHandle.USER_CURRENT); setRotationLock(enabled, NATURAL_ROTATION); }
setRotationLock(final boolean enabled, final int rotation)
调用wm.freezeRotation
或者wm.thawRotation
frameworks/base/core/java/com/android/internal/view/RotationPolicy.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 private static void setRotationLock (final boolean enabled, final int rotation) { AsyncTask.execute(new Runnable() { @Override public void run() { try { IWindowManager wm = WindowManagerGlobal.getWindowManagerService(); if (enabled) { wm.freezeRotation(rotation); } else { wm.thawRotation(); } } catch (RemoteException exc) { Log.w(TAG, "Unable to save auto-rotate setting" ); } } }); }
thawRotation()
,此处在Android Q中有变化。
frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 public void thawRotation () { if (!checkCallingPermission(android.Manifest.permission.SET_ORIENTATION, "thawRotation()" )) { throw new SecurityException("Requires SET_ORIENTATION permission" ); } if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "thawRotation: mRotation=" + getDefaultDisplayRotation()); long origId = Binder.clearCallingIdentity(); try { mPolicy.setUserRotationMode(WindowManagerPolicy.USER_ROTATION_FREE, 777 ); } finally { Binder.restoreCallingIdentity(origId); } updateRotationUnchecked(false , false ); }
而freezeRotation函数
,只是调用PhoneWindowManager的setUserRotationMode的参数不一样,这里是Locked,而thawRotation传下去的参数是free。
frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 @Override public void freezeRotation (int rotation) { if (!checkCallingPermission(android.Manifest.permission.SET_ORIENTATION, "freezeRotation()" )) { throw new SecurityException("Requires SET_ORIENTATION permission" ); } if (rotation < -1 || rotation > Surface.ROTATION_270) { throw new IllegalArgumentException("Rotation argument must be -1 or a valid " + "rotation constant." ); } final int defaultDisplayRotation = getDefaultDisplayRotation(); if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "freezeRotation: mRotation=" + defaultDisplayRotation); long origId = Binder.clearCallingIdentity(); try { mPolicy.setUserRotationMode(WindowManagerPolicy.USER_ROTATION_LOCKED, rotation == -1 ? defaultDisplayRotation : rotation); } finally { Binder.restoreCallingIdentity(origId); } updateRotationUnchecked(false , false ); }
setUserRotationMode(int mode, int rot)
:此处是设置property(即Settings数据库),然后会触发到上面初始化的mSettingsObserver
对象的onChange
函数。
触发监听SettingsObserver.onChange()
, 其中主要调用了updateSettings()和updateRotation()两个函数。
简单地说,主要的工作是根据需要监听传感器数据,据此判断是否要转屏。如果需要就是对configuration的各种更新 。过程中会冻结屏幕,同时截屏并以此作为转屏动画。另外还需要将新configuration传给AMS,广播该事件给需要的模块,同时App也会被调度来响应变更。
frameworks/base/services/core/java/com/android/server/policy/PhoneWindowManager.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 @Override public void setUserRotationMode (int mode, int rot) { ContentResolver res = mContext.getContentResolver(); if (mode == WindowManagerPolicy.USER_ROTATION_LOCKED) { Settings.System.putIntForUser(res, Settings.System.USER_ROTATION, rot, UserHandle.USER_CURRENT); Settings.System.putIntForUser(res, Settings.System.ACCELEROMETER_ROTATION, 0 , UserHandle.USER_CURRENT); } else { Settings.System.putIntForUser(res, Settings.System.ACCELEROMETER_ROTATION, 1 , UserHandle.USER_CURRENT); } } ...... @Override public void onChange (boolean selfChange) { updateSettings(); updateRotation(false ); } ...... public void updateSettings () { ContentResolver resolver = mContext.getContentResolver(); boolean updateRotation = false ; synchronized (mLock) { ... int userRotationMode = Settings.System.getIntForUser(resolver, Settings.System.ACCELEROMETER_ROTATION, 0 , UserHandle.USER_CURRENT) != 0 ? WindowManagerPolicy.USER_ROTATION_FREE : WindowManagerPolicy.USER_ROTATION_LOCKED; if (mUserRotationMode != userRotationMode) { mUserRotationMode = userRotationMode; updateRotation = true ; updateOrientationListenerLp(); }
第一个函数updateSettings()
如它的名字主要更新设置信息。
如果UserRotation(朝向信息,如Surface.ROTATION_0)和UserRotationMode(USER_ROTATION_FREE vs. USER_ROTATION_LOCKED)有更新,就设置标记updateRotation为true,表示接下去需要更新rotation相关信息。
此外,如果UserRotationMode的配置有变,由于需要传感器信息的配合,还需调用updateOrientationListenerLp()来设置或取消监听传感器。
这里假设设置为自动旋转,那么PhoneWindowManager会通过MyOrientationListener来监听传感器信息。MyOrientationListener是WindowOrientationListener的继承类。它的enable()函数中调用SensorManager提供的registerListener()接口来设置Sensor信息的listener。
updateOrientationListenerLp()
:作用是enable和disable传感器
其中的mOrientationListener.enable
和mOrientationListener.disable
是注册传感器回调和去除传感器回调。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 void updateOrientationListenerLp () { if (!mOrientationListener.canDetectOrientation()) { return ; } if (localLOGV) Slog.v(TAG, "mScreenOnEarly=" + mScreenOnEarly + ", mAwake=" + mAwake + ", mCurrentAppOrientation=" + mCurrentAppOrientation + ", mOrientationSensorEnabled=" + mOrientationSensorEnabled + ", mKeyguardDrawComplete=" + mKeyguardDrawComplete + ", mWindowManagerDrawComplete=" + mWindowManagerDrawComplete); boolean disable = true ; if (mScreenOnEarly && mAwake && ((mKeyguardDrawComplete && mWindowManagerDrawComplete))) { if (needSensorRunningLp()) { disable = false ; if (!mOrientationSensorEnabled) { mOrientationListener.enable(true ); if (localLOGV) Slog.v(TAG, "Enabling listeners" ); mOrientationSensorEnabled = true ; } } } if (disable && mOrientationSensorEnabled) { mOrientationListener.disable(); if (localLOGV) Slog.v(TAG, "Disabling listeners" ); mOrientationSensorEnabled = false ; } }
mOrientationListener是MyOrientationListener对象,而MyOrientationListener类继承父类WindowOrientationListener,从而会调用父类的enable
函数。
该函数中会调用registerListener向SensorManager注册一个监听。
frameworks/base/services/core/java/com/android/server/policy/WindowOrientationListener.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 public void enable (boolean clearCurrentRotation) { synchronized (mLock) { if (mSensor == null) { Slog.w(TAG, "Cannot detect sensors. Not enabled" ); return ; } if (mEnabled) { return ; } if (LOG) { Slog.d(TAG, "WindowOrientationListener enabled clearCurrentRotation=" + clearCurrentRotation); } mOrientationJudge.resetLocked(clearCurrentRotation); if (mSensor.getType() == Sensor.TYPE_ACCELEROMETER) { mSensorManager.registerListener( mOrientationJudge, mSensor, mRate, DEFAULT_BATCH_LATENCY, mHandler); } else { mSensorManager.registerListener(mOrientationJudge, mSensor, mRate, mHandler); } mEnabled = true ; } }
registerListener()的具体实现在frameworks/base/core/java/android/hardware/SensorManager.java中。
然后调用SystemSensorManager.java的registerListenerImpl(),其中会创建SensorEventQueue
对象(基类为BaseEventQueue),它是传感器事件的队列,记录需要监听哪些传感器信息。
SensorEventQueue queue = mSensorListeners.get(listener);
同时它也负责与SensorService的连接和通信,可以说是SensorEventListener与SensorService间的桥梁。
SensorEventListener和SensorEventQueue之间是1:1的关系,它们的映射关系保存在成员mSensorListeners中。如果这里注册的SensorEventListener还没有相应的SensorEventQueue,则新建一个,然后通过addSensor()方法将要关注的传感器进行注册。这个过程中addSensor()调用了enableSensor(),它最终是通过SensorService的enableDisable()方法来完成注册工作的。
这样,SensorService就开始监听该Sensor,当底层有传感器数据来时,SensorService主线程中会调用相应SensorEventConnection的sendEvents()将之发给对应的Client。
前面初始化SensorEventQueue时会创建Receiver,它是一个Looper的回调对象,在Client端收到从SensorService来的数据后被回调。
当有数据收到时Receiver的handleEvent()被调用,继而通过JNI调用到SystemSensorManager::dispatchSensorEvent()。
接着就调到了WindowOrientationListener的onSensorChanged()函数
。该函数计算是否需要转屏。如果需要转屏,将计算结果传给onProposedRotationChanged()。
比如以下函数的日志打印,在旋转手机,传感器会触发屏幕旋转打印这部分log:
frameworks/base/services/core/java/com/android/server/policy/WindowOrientationListener.java 1 2 3 4 5 6 7 8 9 10 11 12 @Override public void onSensorChanged (SensorEvent event) { ...... if (proposedRotation != oldProposedRotation && proposedRotation >= 0 ) { if (LOG) { Slog.v(TAG, "Proposed rotation changed! proposedRotation=" + proposedRotation + ", oldProposedRotation=" + oldProposedRotation); } onProposedRotationChanged(proposedRotation); } }
另一处updateRotation(false)
函数会调用到WMS.java,然后调用到updateRotationUnchecked
函数。
最终在该函数中调用rotationChanged = displayContent.updateRotationUnchecked();
屏幕旋转 假设现在用户转了屏幕,期望转屏事件发生。如上面第九步的代码,onProposedRotationChanged()
被调用。
最后就调用其run函数,run函数先会提升性能(cpu频率),然后调用了updateRotation,这个函数一样就到WMS的updateRotationUnchecked函数。
frameworks/base/services/core/java/com/android/server/policy/PhoneWindowManager.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 class MyOrientationListener extends WindowOrientationListener {... private class UpdateRunnable implements Runnable { private final int mRotation; UpdateRunnable(int rotation) { mRotation = rotation; } @Override public void run () { mPowerManagerInternal.powerHint(PowerHint.INTERACTION, 0 ); if (isRotationChoicePossible(mCurrentAppOrientation)) { final boolean isValid = isValidRotationChoice(mCurrentAppOrientation, mRotation); sendProposedRotationChangeToStatusBarInternal(mRotation, isValid); } else { updateRotation(false ); } } } @Override public void onProposedRotationChanged (int rotation) { if (localLOGV) Slog.v(TAG, "onProposedRotationChanged, rotation=" + rotation); Runnable r = mRunnableCache.get(rotation, null ); if (r == null ){ r = new UpdateRunnable(rotation); mRunnableCache.put(rotation, r); } mHandler.post(r); } }
updateRotation()中主要是执行两个函数:updateRotationUnchecked()(displayContent.updateRotationUnchecked()
)和sendNewConfiguration()。前者执行转屏动作,包含转屏动画 等。后者使AMS获取当前新的configuration,并且广播该事件给所有相应的listener。
frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 @Override public void updateRotation (boolean alwaysSendConfiguration, boolean forceRelayout) { updateRotationUnchecked(alwaysSendConfiguration, forceRelayout); } private void updateRotationUnchecked (boolean alwaysSendConfiguration, boolean forceRelayout) { if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "updateRotationUnchecked:" + " alwaysSendConfiguration=" + alwaysSendConfiguration + " forceRelayout=" + forceRelayout); ... try { final boolean rotationChanged; final int displayId; synchronized (mWindowMap) { final DisplayContent displayContent = getDefaultDisplayContentLocked(); Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "updateRotation: display" ); rotationChanged = displayContent.updateRotationUnchecked(); ... } if (rotationChanged || alwaysSendConfiguration) { Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "updateRotation: sendNewConfiguration" ); sendNewConfiguration(displayId); Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); } } finally { Binder.restoreCallingIdentity(origId); Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); } }
Note: : 其它途径可能会触发转屏,比如应用请求转屏。例如需要横屏的游戏(通过frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java的updateOrientationFromAppTokensLocked()
方法)。
updateRotationUnchecked函数 frameworks/base/services/core/java/com/android/server/wm/DisplayContent.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 boolean updateRotationUnchecked (boolean forceUpdate) { final int oldRotation = mRotation; final int lastOrientation = mLastOrientation; final boolean oldAltOrientation = mAltOrientation; final int rotation = mService.mPolicy.rotationForOrientationLw(lastOrientation, oldRotation, isDefaultDisplay); if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Computed rotation=" + rotation + " for display id=" + mDisplayId + " based on lastOrientation=" + lastOrientation + " and oldRotation=" + oldRotation); ... if (oldRotation == rotation && oldAltOrientation == altOrientation) { return false ; } ... mRotation = rotation; mAltOrientation = altOrientation; ... updateDisplayAndOrientation(getConfiguration().uiMode); ... mService.mDisplayManagerInternal.performTraversal(getPendingTransaction()); ... }
updateDisplayAndOrientation函数 还会调用到updateDisplayAndOrientation函数,会把各种数据更新下放到DisplayInfo中
,最后调用了DisplayManagerService的setDisplayInfoOverrideFromWindowManager
函数。
frameworks/base/services/core/java/com/android/server/wm/DisplayContent.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 private DisplayInfo updateDisplayAndOrientation (int uiMode) { final boolean rotated = (mRotation == ROTATION_90 || mRotation == ROTATION_270); final int realdw = rotated ? mBaseDisplayHeight : mBaseDisplayWidth; final int realdh = rotated ? mBaseDisplayWidth : mBaseDisplayHeight; int dw = realdw; int dh = realdh; ... final int appWidth = mService.mPolicy.getNonDecorDisplayWidth(dw, dh, mRotation, uiMode, mDisplayId, displayCutout); final int appHeight = mService.mPolicy.getNonDecorDisplayHeight(dw, dh, mRotation, uiMode, mDisplayId, displayCutout); mDisplayInfo.rotation = mRotation; mDisplayInfo.logicalWidth = dw; mDisplayInfo.logicalHeight = dh; mDisplayInfo.logicalDensityDpi = mBaseDisplayDensity; mDisplayInfo.appWidth = appWidth; mDisplayInfo.appHeight = appHeight; if (isDefaultDisplay) { mDisplayInfo.getLogicalMetrics(mRealDisplayMetrics, CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null ); } mDisplayInfo.displayCutout = displayCutout.isEmpty() ? null : displayCutout; mDisplayInfo.getAppMetrics(mDisplayMetrics); if (mDisplayScalingDisabled) { mDisplayInfo.flags |= Display.FLAG_SCALING_DISABLED; } else { mDisplayInfo.flags &= ~Display.FLAG_SCALING_DISABLED; } ... mService.mDisplayManagerInternal.setDisplayInfoOverrideFromWindowManager(mDisplayId, overrideDisplayInfo); ... }
setDisplayInfoOverrideFromWindowManager会调用setDisplayInfoOverrideFromWindowManagerInternal,然后调用display.setDisplayInfoOverrideFromWindowManagerLocked(info)
函数,最后到LogicalDisplay的setDisplayInfoOverrideFromWindowManagerLocked函数中,把DisplayInfo数据放到了mOverrideDisplayInfo中。
frameworks/base/services/core/java/com/android/server/display/LogicalDisplay.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 public boolean setDisplayInfoOverrideFromWindowManagerLocked (DisplayInfo info) { if (info != null ) { if (mOverrideDisplayInfo == null ) { mOverrideDisplayInfo = new DisplayInfo(info); mInfo = null ; return true ; } if (!mOverrideDisplayInfo.equals(info)) { mOverrideDisplayInfo.copyFrom(info); mInfo = null ; return true ; } } else if (mOverrideDisplayInfo != null ) { mOverrideDisplayInfo = null ; mInfo = null ; return true ; } return false ; }
调用到DisplayManagerService.java中,然后调用performTraversalInternal函数。
frameworks/base/services/core/java/com/android/server/display/DisplayManagerService.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 void performTraversalInternal (SurfaceControl.Transaction t) { synchronized (mSyncRoot) { if (!mPendingTraversal) { return ; } mPendingTraversal = false ; performTraversalLocked(t); } for (DisplayTransactionListener listener : mDisplayTransactionListeners) { listener.onDisplayTransaction(); } } private void performTraversalLocked (SurfaceControl.Transaction t) { clearViewportsLocked(); final int count = mDisplayDevices.size(); for (int i = 0 ; i < count; i++) { DisplayDevice device = mDisplayDevices.get(i); configureDisplayLocked(t, device); device.performTraversalLocked(t); } if (mInputManagerInternal != null ) { mHandler.sendEmptyMessage(MSG_UPDATE_VIEWPORT); } } private void configureDisplayLocked (SurfaceControl.Transaction t, DisplayDevice device) { ... display.configureDisplayLocked(t, device, info.state == Display.STATE_OFF); ... }
configureDisplayLocked函数的这部分代码就是设置layer的显示大小,例如viewport,通过Dump SF可以查看layer。
frameworks/base/services/core/java/com/android/server/display/LogicalDisplay.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 public void configureDisplayLocked (SurfaceControl.Transaction t, DisplayDevice device, boolean isBlanked) { ... final DisplayInfo displayInfo = getDisplayInfoLocked(); final DisplayDeviceInfo displayDeviceInfo = device.getDisplayDeviceInfoLocked(); mTempLayerStackRect.set(0 , 0 , displayInfo.logicalWidth, displayInfo.logicalHeight); ... mTempDisplayRect.left += mDisplayOffsetX; mTempDisplayRect.right += mDisplayOffsetX; mTempDisplayRect.top += mDisplayOffsetY; mTempDisplayRect.bottom += mDisplayOffsetY; device.setProjectionLocked(t, orientation, mTempLayerStackRect, mTempDisplayRect); } public DisplayInfo getDisplayInfoLocked () { if (mInfo == null ) { mInfo = new DisplayInfo(); mInfo.copyFrom(mBaseDisplayInfo); if (mOverrideDisplayInfo != null ) { mInfo.appWidth = mOverrideDisplayInfo.appWidth; mInfo.appHeight = mOverrideDisplayInfo.appHeight; mInfo.smallestNominalAppWidth = mOverrideDisplayInfo.smallestNominalAppWidth; mInfo.smallestNominalAppHeight = mOverrideDisplayInfo.smallestNominalAppHeight; mInfo.largestNominalAppWidth = mOverrideDisplayInfo.largestNominalAppWidth; mInfo.largestNominalAppHeight = mOverrideDisplayInfo.largestNominalAppHeight; mInfo.logicalWidth = mOverrideDisplayInfo.logicalWidth; mInfo.logicalHeight = mOverrideDisplayInfo.logicalHeight; mInfo.overscanLeft = mOverrideDisplayInfo.overscanLeft; mInfo.overscanTop = mOverrideDisplayInfo.overscanTop; mInfo.overscanRight = mOverrideDisplayInfo.overscanRight; mInfo.overscanBottom = mOverrideDisplayInfo.overscanBottom; mInfo.rotation = mOverrideDisplayInfo.rotation; mInfo.displayCutout = mOverrideDisplayInfo.displayCutout; mInfo.logicalDensityDpi = mOverrideDisplayInfo.logicalDensityDpi; mInfo.physicalXDpi = mOverrideDisplayInfo.physicalXDpi; mInfo.physicalYDpi = mOverrideDisplayInfo.physicalYDpi; } } return mInfo; }
setProjectionLocked会调用SurfaceControl的SurfaceControl函数。然后在SurfaceControl中调用nativeSetDisplayProjection函数,通过JNI调用到Native层。
frameworks/base/services/core/java/com/android/server/display/DisplayDevice.java 1 2 3 4 5 6 7 public final void setProjectionLocked (SurfaceControl.Transaction t, int orientation, Rect layerStackRect, Rect displayRect) {... t.setDisplayProjection(mDisplayToken, orientation, layerStackRect, displayRect); } }
此时Java层的updateRotationUnchecked
函数分析完。
sendNewConfiguration函数 从上面的updateRotation()函数中看到,除了调用updateRotationUnchecked()(即displayContent.updateRotationUnchecked()
),还会调用sendNewConfiguration()。
frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java 1 2 3 4 5 6 7 8 9 void sendNewConfiguration (int displayId) { try { final boolean configUpdated = mActivityManager.updateDisplayOverrideConfiguration( null , displayId); ... } } catch (RemoteException e) { } }
frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 @Override public boolean updateDisplayOverrideConfiguration (Configuration values, int displayId) { enforceCallingPermission(CHANGE_CONFIGURATION, "updateDisplayOverrideConfiguration()" ); synchronized (this ) { ... if (values == null && mWindowManager != null ) { values = mWindowManager.computeNewConfiguration(displayId); } final long origId = Binder.clearCallingIdentity(); try { if (values != null ) { Settings.System.clearConfiguration(values); } updateDisplayOverrideConfigurationLocked(values, null , false , displayId, mTmpUpdateConfigurationResult); return mTmpUpdateConfigurationResult.changes != 0 ; } finally { Binder.restoreCallingIdentity(origId); } } } private boolean updateDisplayOverrideConfigurationLocked (Configuration values, ActivityRecord starting, boolean deferResume, int displayId, UpdateConfigurationResult result) { ... try { if (values != null ) { if (displayId == DEFAULT_DISPLAY) { changes = updateGlobalConfigurationLocked(values, false , false , UserHandle.USER_NULL , deferResume); } else { changes = performDisplayOverrideConfigUpdate(values, deferResume, displayId); } } kept = ensureConfigAndVisibilityAfterUpdate(starting, changes); } finally { if (mWindowManager != null ) { mWindowManager.continueSurfaceLayout(); } } if (result != null ) { result.changes = changes; result.activityRelaunched = !kept; } return kept; } private int updateGlobalConfigurationLocked (@NonNull Configuration values, boolean initLocale, boolean persistent, int userId, boolean deferResume) { mTempConfig.setTo(getGlobalConfiguration()); final int changes = mTempConfig.updateFrom(values); if (changes == 0 ) { performDisplayOverrideConfigUpdate(values, deferResume, DEFAULT_DISPLAY); return 0 ; } Intent intent = new Intent(Intent.ACTION_CONFIGURATION_CHANGED); intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY | Intent.FLAG_RECEIVER_REPLACE_PENDING | Intent.FLAG_RECEIVER_FOREGROUND | Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS); broadcastIntentLocked(null , null , intent, null , null , 0 , null , null , null , OP_NONE, null , false , false , MY_PID, SYSTEM_UID, UserHandle.USER_ALL); ...... } private int performDisplayOverrideConfigUpdate (Configuration values, boolean deferResume, int displayId) { mTempConfig.setTo(mStackSupervisor.getDisplayOverrideConfiguration(displayId)); final int changes = mTempConfig.updateFrom(values); ... } private boolean ensureConfigAndVisibilityAfterUpdate (ActivityRecord starting, int changes) { boolean kept = true ; final ActivityStack mainStack = mStackSupervisor.getFocusedStack(); if (mainStack != null ) { if (changes != 0 && starting == null ) { starting = mainStack.topRunningActivityLocked(); } if (starting != null ) { kept = starting.ensureActivityConfiguration(changes, false ); mStackSupervisor.ensureActivitiesVisibleLocked(starting, changes, !PRESERVE_WINDOWS); } } return kept; }
应用强制设置屏幕方向 之前提过,其它途径可能会触发转屏,比如应用请求转屏。例如需要横屏的游戏(通过frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java的updateOrientationFromAppTokensLocked()
方法)。
首先调用AMS的setRequestedOrientation
函数,然后调用到ActivityRecord的setRequestedOrientation函数。
frameworks/base/services/core/java/com/android/server/am/ActivityRecord.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 void setRequestedOrientation (int requestedOrientation) { final int displayId = getDisplayId(); final Configuration displayConfig = mStackSupervisor.getDisplayOverrideConfiguration(displayId); final Configuration config = mWindowContainerController.setOrientation(requestedOrientation, displayId, displayConfig, mayFreezeScreenLocked(app)); if (config != null ) { frozenBeforeDestroy = true ; if (!service.updateDisplayOverrideConfigurationLocked(config, this , false , displayId)) { mStackSupervisor.resumeFocusedStackTopActivityLocked(); } } service.mTaskChangeNotificationController.notifyActivityRequestedOrientationChanged( task.taskId, requestedOrientation); }
其中调用到frameworks/base/services/core/java/com/android/server/wm/AppWindowContainerController.java
的mWindowContainerController.setOrientation
函数。
frameworks/base/services/core/java/com/android/server/wm/AppWindowContainerController.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public Configuration setOrientation (int requestedOrientation, int displayId, Configuration displayConfig, boolean freezeScreenIfNeeded) { synchronized (mWindowMap) { if (mContainer == null ) { Slog.w(TAG_WM, "Attempted to set orientation of non-existing app token: " + mToken); return null ; } mContainer.setOrientation(requestedOrientation); final IBinder binder = freezeScreenIfNeeded ? mToken.asBinder() : null ; return mService.updateOrientationFromAppTokens(displayConfig, binder, displayId); } }
该函数调用到WMS的updateOrientationFromAppTokensLocked
函数。这个函数先调用另一个updateOrientationFromAppTokensLocked函数,根据这个函数的返回值,返回true代表要旋转,就调用computeNewConfigurationLocked计算Configuration返回。
WMS.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 private Configuration updateOrientationFromAppTokensLocked (Configuration currentConfig, IBinder freezeThisOneIfNeeded, int displayId, boolean forceUpdate) { if (!mDisplayReady) { return null ; } Configuration config = null ; if (updateOrientationFromAppTokensLocked(displayId, forceUpdate)) { if (freezeThisOneIfNeeded != null && !mRoot.mOrientationChangeComplete) { final AppWindowToken atoken = mRoot.getAppWindowToken(freezeThisOneIfNeeded); if (atoken != null ) { atoken.startFreezingScreen(); } } config = computeNewConfigurationLocked(displayId); } else if (currentConfig != null ) { ...... } } return config; } boolean updateOrientationFromAppTokensLocked (int displayId, boolean forceUpdate) { long ident = Binder.clearCallingIdentity(); try { final DisplayContent dc = mRoot.getDisplayContent(displayId); final int req = dc.getOrientation(); if (req != dc.getLastOrientation() || forceUpdate) { dc.setLastOrientation(req); if (dc.isDefaultDisplay) { mPolicy.setCurrentOrientationLw(req); } return dc.updateRotationUnchecked(forceUpdate); } return false ; } finally { Binder.restoreCallingIdentity(ident); } }
应用Activity强制设置方向
Activity:
如果要强制设置一个Activity的横竖屏可以通过Manifest去设置,跟Activity相关的信息都会保存在ActivityInfo当中。
1 2 3 4 5 android:screenOrientation=["unspecified" | "user" | "behind" | "landscape" | "portrait" | "reverseLandscape" | "reversePortrait" | "sensorLandscape" | "sensorPortrait" | "sensor" | "fullSensor" | "nosensor"]
Window
如果是要强制设置一个Window的横竖屏可以通过LayoutParams.screenOrientation
来设置。在通过WindowManager.addView的时候把对应的LayoutParams
传递给WMS。
1 WindowManager.LayoutParams.screenOrientation