问题现象
- 版本:android P + Docker
- 现象:运行王者荣耀游戏,在付款页面返回游戏页面时,发生竖屏到横屏的切换,游戏页面显示不正常,仅显示半个页面,右半面显示黑色。
分析
首先是横竖屏切换的代码,主要是在windowsmanagerservice里面处理的。
参考了:
Android 7.1 屏幕旋转流程分析
Android 7.1 WindowManagerService 屏幕旋转流程分析 (二)
Android 7.1 WindowManagerService 屏幕旋转流程分析 (三)
Android 7.1 ActivityManagerService 屏幕旋转流程分析 (四)
WindowManagerService 大致完成三件事,首先更新屏幕方向,然后具体实施屏幕旋转,最后通知AMS configuration变更。
framework/base/services/core/java/com/android/server/wm/WindowManagerService.java
3858: private void updateRotationUnchecked(boolean alwaysSendConfiguration, boolean forceRelayout) {
这个函数即首先调用displayContent.updateRotationUnchecked();更新rotation,然后调用performSurfacePlacement()做屏幕的绘制,最后调用sendNewConfiguration()发送Configuration变更事件。
发现问题
- 通过dumpsys SurfaceFlinger 看layer,发现有问题的layer显示区域不对,应该显示横屏的仍然以竖屏方式显示
- 由于这个问题是偶现的问题,开始以为是performance的问题,在显示绘制前加了100ms的延迟,问题可以解决,但切换时卡顿严重:
frameworks/base/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
148: final void performSurfacePlacement(boolean force) {
…
int loopCount = 6;
/* try {
Thread.sleep(100);
} catch (Exception e) {
e.printStackTrace();
}
*/
do {
mTraversalScheduled = false;
-
因为最终是在SurfaceFlinger里面进行合成显示,需要在java和native间调用切换,通过增加log,最终发现是请求切换的高宽没有送到native层
正确log: 10-16 10:31:23.744 141 141 E Layer : doTransaction geometry (layer=0x714a93067000 ‘com.tencent.tmgp.sgame/com.tencent.midas.proxyactivity.APMidasPayProxyActivity#1’), req(1280, 672) >cur (720, 1232), sizeChanged: 1
错误log: 10-16 10:31:23.744 141 141 E Layer : doTransaction geometry (layer=0x714a93067000 ‘com.tencent.tmgp.sgame/com.tencent.midas.proxyactivity.APMidasPayProxyActivity#1’), req(720, 1280) >cur (720, 1232), sizeChanged: 1 -
向上跟踪, 发现是setSize没有执行:
framework/nativelibs/gui/SurfaceComposerClient.cpp
SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setSize(
const sp& sc, uint32_t w, uint32_t h) {
- 这个是native的api, base里面调过来的,在setSurfaceBoundariesLocked里面有个开关mInRelayout, 发生错误的时候这个值是false。
services/core/java/com/android/server/wm/WindowStateAnimator.java
void setSurfaceBoundariesLocked(final boolean recoveringMemory) {
…
final boolean relayout = !w.mRelayoutCalled || w.mInRelayout;
if (relayout) {
mSurfaceResized = mSurfaceController.setSizeInTransaction(
mTmpSize.width(), mTmpSize.height(), recoveringMemory);
} else {
解决问题
- 增加一个tag,判断其页面高宽发生变化后更新size。
- patch:
在这里插入代码片
- …/java/com/android/server/wm/WindowManagerService.java | 3 +++
…/core/java/com/android/server/wm/WindowState.java | 9 +++++++++
…/java/com/android/server/wm/WindowStateAnimator.java | 2 ±
3 files changed, 13 insertions(+), 1 deletion(-)
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 7a2c28bd5c8…730afb59227 100644
— a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -1975,6 +1975,9 @@ public class WindowManagerService extends IWindowManager.Stub
win.mRelayoutCalled = true;
win.mInRelayout = true;
-
if (requestedWidth != win.mFrame.width() || requestedWidth != win.mRequestedWidth ||win.isNeedReLayout()) {
-
win.mNeedRelayout = true;
-
} win.mViewVisibility = viewVisibility; if (DEBUG_SCREEN_ON) {
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index bee70a01194…d2d7984529e 100644
— a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -475,6 +475,8 @@ class WindowState extends WindowContainer implements WindowManagerP
boolean mInRelayout;
-
boolean mNeedRelayout;
-
/**
* If the application has called relayout() with changes that can
* impact its window’s size, we need to perform a layout pass on it
@@ -1550,6 +1552,13 @@ class WindowState extends WindowContainer implements WindowManagerP
&& !mAnimatingExit && !mDestroying;
} -
boolean isNeedReLayout() {
-
if (mRequestedWidth != mLastRequestedWidth || mRequestedHeight != mLastRequestedHeight) {
-
return true;
-
}
-
return false;
-
}
-
/**
* Is this window currently on-screen? It is on-screen either if it
* is visible or it is currently running an animation before no longer
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 410e1587924…beb8479d5bf 100644
— a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -888,7 +888,7 @@ class WindowStateAnimator {
// However, this would be unsafe, as the client may be in the middle
// of producing a frame at the old size, having just completed layout
// to find the surface size changed underneath it.
-
final boolean relayout = !w.mRelayoutCalled || w.mInRelayout;
-
final boolean relayout = !w.mRelayoutCalled || w.mInRelayout || w.mNeedRelayout; if (relayout) { mSurfaceResized = mSurfaceController.setSizeInTransaction( mTmpSize.width(), mTmpSize.height(), recoveringMemory);
–
2.17.1