From f858b5788e5bcc9a84633a77daf4d42cb5de9375 Mon Sep 17 00:00:00 2001 From: Bryce Lee Date: Thu, 29 Jun 2017 14:03:33 -0700 Subject: [PATCH] Always send configuration to client from resize and relayout. Previously, the server side would omit reporting new configurations during resize and relayout if the current configuration matched the last reported. However, if the last reported came through a resize, the client side handling would be enqueued as a Handler message. This leads to the possibility of the client getting a new window frame but not a new configuration where this is synchronously handled, such as ViewRootImpl::performTraversals. To address this issue, we now always send the current configuration from the server to the client. The client then identifies changes against its own record of last reported configuration and updates as appropriate. This changelist also adds a call to force window relayout during updateConfiguration, as it's possible this is called after performTraversals is called or resize is handled, leading to a stale window frame. Bug: 24671393 Test: go/wm-smoke Test: Open Camera while rotating phone to landscape. Added temporary logs to detect inconsistencies between measurements and reported rotation on draw. Change-Id: I0d5143dfe80400f6a43ce710750f9fc9d4b93f74 --- .../android/util/MergedConfiguration.java | 15 +++++++ core/java/android/view/ViewRootImpl.java | 25 ++++++++---- .../server/wm/WindowManagerService.java | 15 ++++--- .../com/android/server/wm/WindowState.java | 40 +++++++++---------- .../android/server/wm/WindowStateTests.java | 3 +- 5 files changed, 61 insertions(+), 37 deletions(-) diff --git a/core/java/android/util/MergedConfiguration.java b/core/java/android/util/MergedConfiguration.java index 68d0309cfc9e8..ae6605043b2ea 100644 --- a/core/java/android/util/MergedConfiguration.java +++ b/core/java/android/util/MergedConfiguration.java @@ -161,6 +161,21 @@ public class MergedConfiguration implements Parcelable { return "{mGlobalConfig=" + mGlobalConfig + " mOverrideConfig=" + mOverrideConfig + "}"; } + @Override + public int hashCode() { + return mMergedConfig.hashCode(); + } + + @Override + public boolean equals(Object that) { + if (!(that instanceof MergedConfiguration)) { + return false; + } + + if (that == this) return true; + return mMergedConfig.equals(((MergedConfiguration) that).mMergedConfig); + } + public void dump(PrintWriter pw, String prefix) { pw.println(prefix + "mGlobalConfig=" + mGlobalConfig); pw.println(prefix + "mOverrideConfig=" + mOverrideConfig); diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index e27eab92319bc..e38a55f76b7db 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -1904,14 +1904,16 @@ public final class ViewRootImpl implements ViewParent, + " outsets=" + mPendingOutsets.toShortString() + " surface=" + mSurface); - final Configuration pendingMergedConfig = - mPendingMergedConfiguration.getMergedConfiguration(); - if (pendingMergedConfig.seq != 0) { + // If the pending {@link MergedConfiguration} handed back from + // {@link #relayoutWindow} does not match the one last reported, + // WindowManagerService has reported back a frame from a configuration not yet + // handled by the client. In this case, we need to accept the configuration so we + // do not lay out and draw with the wrong configuration. + if (!mPendingMergedConfiguration.equals(mLastReportedMergedConfiguration)) { if (DEBUG_CONFIGURATION) Log.v(mTag, "Visible with new config: " - + pendingMergedConfig); + + mPendingMergedConfiguration.getMergedConfiguration()); performConfigurationChange(mPendingMergedConfiguration, !mFirst, INVALID_DISPLAY /* same display */); - pendingMergedConfig.seq = 0; updatedConfiguration = true; } @@ -3596,6 +3598,13 @@ public final class ViewRootImpl implements ViewParent, mView.setLayoutDirection(currentLayoutDirection); } mView.dispatchConfigurationChanged(config); + + // We could have gotten this {@link Configuration} update after we called + // {@link #performTraversals} with an older {@link Configuration}. As a result, our + // window frame may be stale. We must ensure the next pass of {@link #performTraversals} + // catches this. + mForceNextWindowRelayout = true; + requestLayout(); } } @@ -3757,10 +3766,10 @@ public final class ViewRootImpl implements ViewParent, SomeArgs args = (SomeArgs) msg.obj; final int displayId = args.argi3; - final MergedConfiguration mergedConfiguration = (MergedConfiguration) args.arg4; + MergedConfiguration mergedConfiguration = (MergedConfiguration) args.arg4; final boolean displayChanged = mDisplay.getDisplayId() != displayId; - if (mergedConfiguration != null) { + if (!mLastReportedMergedConfiguration.equals(mergedConfiguration)) { // If configuration changed - notify about that and, maybe, about move to // display. performConfigurationChange(mergedConfiguration, false /* force */, @@ -6094,7 +6103,7 @@ public final class ViewRootImpl implements ViewParent, if (params != null) { if (DBG) Log.d(mTag, "WindowLayout in layoutWindow:" + params); } - mPendingMergedConfiguration.getMergedConfiguration().seq = 0; + //Log.d(mTag, ">>>>>> CALLING relayout"); if (params != null && mOrigWindowType != params.type) { // For compatibility with old apps, don't crash here. diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index ceddc4e23fe53..613c830f31f24 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -30,7 +30,6 @@ import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static android.os.Process.ROOT_UID; import static android.os.Process.SHELL_UID; import static android.os.Process.SYSTEM_UID; -import static android.os.Process.THREAD_PRIORITY_DISPLAY; import static android.os.Process.myPid; import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER; import static android.os.UserHandle.USER_NULL; @@ -50,7 +49,6 @@ import static android.view.WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHA import static android.view.WindowManager.LayoutParams.LAST_APPLICATION_WINDOW; import static android.view.WindowManager.LayoutParams.LAST_SUB_WINDOW; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_COMPATIBLE_WINDOW; -import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_TASK_SNAPSHOT; import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING; import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER; @@ -233,7 +231,6 @@ import com.android.server.DisplayThread; import com.android.server.EventLogTags; import com.android.server.FgThread; import com.android.server.LocalServices; -import com.android.server.ThreadPriorityBooster; import com.android.server.UiThread; import com.android.server.Watchdog; import com.android.server.input.InputManagerService; @@ -2020,8 +2017,7 @@ public class WindowManagerService extends IWindowManager.Stub win.setDisplayLayoutNeeded(); mWindowPlacerLocked.performSurfacePlacement(true); } - result = win.relayoutVisibleWindow(mergedConfiguration, result, attrChanges, - oldVisibility); + result = win.relayoutVisibleWindow(result, attrChanges, oldVisibility); try { result = createSurfaceControl(outSurface, result, win, winAnimator); @@ -2159,6 +2155,15 @@ public class WindowManagerService extends IWindowManager.Stub if (!win.isGoneForLayoutLw()) { win.mResizedWhileGone = false; } + + // We must always send the latest {@link MergedConfiguration}, regardless of whether we + // have already reported it. The client might not have processed the previous value yet + // and needs process it before handling the corresponding window frame. the variable + // {@code mergedConfiguration} is an out parameter that will be passed back to the + // client over IPC and checked there. + win.getMergedConfiguration(mergedConfiguration); + win.setReportedConfiguration(mergedConfiguration); + outFrame.set(win.mCompatFrame); outOverscanInsets.set(win.mOverscanInsets); outContentInsets.set(win.mContentInsets); diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index f74948f8e7f06..d1fbf900ef70e 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -2207,8 +2207,7 @@ class WindowState extends WindowContainer implements WindowManagerP } } - void prepareWindowToDisplayDuringRelayout(MergedConfiguration mergedConfiguration, - boolean wasVisible) { + void prepareWindowToDisplayDuringRelayout(boolean wasVisible) { // We need to turn on screen regardless of visibility. if ((mAttrs.flags & FLAG_TURN_SCREEN_ON) != 0) { if (DEBUG_VISIBILITY) Slog.v(TAG, "Relayout window turning screen on: " + this); @@ -2230,16 +2229,16 @@ class WindowState extends WindowContainer implements WindowManagerP if (isDrawnLw() && mService.okToDisplay()) { mWinAnimator.applyEnterAnimationLocked(); } + } - if (isConfigChanged()) { - final Configuration globalConfig = mService.mRoot.getConfiguration(); - final Configuration overrideConfig = getMergedOverrideConfiguration(); - mergedConfiguration.setConfiguration(globalConfig, overrideConfig); - if (DEBUG_CONFIGURATION) Slog.i(TAG, "Window " + this - + " visible with new global config: " + globalConfig - + " merged override config: " + overrideConfig); - mLastReportedConfiguration.setTo(getConfiguration()); - } + void getMergedConfiguration(MergedConfiguration outConfiguration) { + final Configuration globalConfig = mService.mRoot.getConfiguration(); + final Configuration overrideConfig = getMergedOverrideConfiguration(); + outConfiguration.setConfiguration(globalConfig, overrideConfig); + } + + void setReportedConfiguration(MergedConfiguration config) { + mLastReportedConfiguration.setTo(config.getMergedConfiguration()); } void adjustStartingWindowFlags() { @@ -3005,14 +3004,12 @@ class WindowState extends WindowContainer implements WindowManagerP try { if (DEBUG_RESIZE || DEBUG_ORIENTATION) Slog.v(TAG, "Reporting new frame to " + this + ": " + mCompatFrame); - final MergedConfiguration mergedConfiguration; - if (isConfigChanged()) { - mergedConfiguration = new MergedConfiguration(mService.mRoot.getConfiguration(), - getMergedOverrideConfiguration()); - mLastReportedConfiguration.setTo(mergedConfiguration.getMergedConfiguration()); - } else { - mergedConfiguration = null; - } + final MergedConfiguration mergedConfiguration = + new MergedConfiguration(mService.mRoot.getConfiguration(), + getMergedOverrideConfiguration()); + + setReportedConfiguration(mergedConfiguration); + if (DEBUG_ORIENTATION && mWinAnimator.mDrawState == DRAW_PENDING) Slog.i(TAG, "Resizing " + this + " WITH DRAW PENDING"); @@ -4342,8 +4339,7 @@ class WindowState extends WindowContainer implements WindowManagerP return !mLastSurfaceInsets.equals(mAttrs.surfaceInsets); } - int relayoutVisibleWindow(MergedConfiguration mergedConfiguration, int result, int attrChanges, - int oldVisibility) { + int relayoutVisibleWindow(int result, int attrChanges, int oldVisibility) { final boolean wasVisible = isVisibleLw(); result |= (!wasVisible || !isDrawnLw()) ? RELAYOUT_RES_FIRST_TIME : 0; @@ -4366,7 +4362,7 @@ class WindowState extends WindowContainer implements WindowManagerP mWinAnimator.mEnteringAnimation = true; - prepareWindowToDisplayDuringRelayout(mergedConfiguration, wasVisible); + prepareWindowToDisplayDuringRelayout(wasVisible); if ((attrChanges & FORMAT_CHANGED) != 0) { // If the format can't be changed in place, preserve the old surface until the app draws diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java b/services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java index c809c325726ff..67db5f4d12dda 100644 --- a/services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java +++ b/services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java @@ -218,8 +218,7 @@ public class WindowStateTests extends WindowTestsBase { root.mAttrs.flags |= WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON; root.mTurnOnScreen = false; - root.prepareWindowToDisplayDuringRelayout(new MergedConfiguration(), - wasVisible /*wasVisible*/); + root.prepareWindowToDisplayDuringRelayout(wasVisible /*wasVisible*/); assertTrue(root.mTurnOnScreen); } }