From c5f95fb5a5fa348ae49afb0ca3d3620155a67678 Mon Sep 17 00:00:00 2001 From: Riddle Hsu Date: Fri, 16 Apr 2021 15:30:58 +0800 Subject: [PATCH] Update config from ancestor if it can handle orientation request When calling Activity#setRequestedOrientation, the orientation of task may not be updated yet. If the requested orientation are mismatched, the configuration could be letterboxed (unresizable activity) according to the intermediate state. And because WindowProcessController may be a configuration listener of the activity, the app may receive unexpected configuration and keep calling setRequestedOrientation to cause a relaunch loop. Now the direct onConfigurationChanged from setOrientation is only called when: - In multi window mode: if the activity is unresizable, its requested orientation should be still respected to compute letterboxed bounds if needed. Otherwise the orientation is ignored for resizable. - The ancestor ignores orientation request from descendant, e.g. in a fixed orientation display. This should save an extra onConfigurationChanged for the most common case in fullscreen windowing mode. Fixes: 169349616 Test: atest WindowContainerTests#testSetOrientation Change-Id: I49ba2003699422a491c1183b10a691e25867ffa7 --- .../com/android/server/wm/WindowContainer.java | 9 ++++++++- .../android/server/wm/WindowContainerTests.java | 17 +++++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java index 191c3a117e851..bea733b0267e7 100644 --- a/services/core/java/com/android/server/wm/WindowContainer.java +++ b/services/core/java/com/android/server/wm/WindowContainer.java @@ -1257,7 +1257,14 @@ class WindowContainer extends ConfigurationContainer< mOrientation = orientation; final WindowContainer parent = getParent(); if (parent != null) { - if (getConfiguration().orientation != getRequestedConfigurationOrientation()) { + if (getConfiguration().orientation != getRequestedConfigurationOrientation() + // Update configuration directly only if the change won't be dispatched from + // ancestor. This prevents from computing intermediate configuration when the + // parent also needs to be updated from the ancestor. E.g. the app requests + // portrait but the task is still in landscape. While updating from display, + // the task can be updated to portrait first so the configuration can be + // computed in a consistent environment. + && (inMultiWindowMode() || !handlesOrientationChangeFromDescendant())) { // Resolve the requested orientation. onConfigurationChanged(parent.getConfiguration()); } diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java index 6919c4cf6a7c4..00f3d8b874f73 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java @@ -16,6 +16,7 @@ package com.android.server.wm; +import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_BEHIND; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT; @@ -633,6 +634,22 @@ public class WindowContainerTests extends WindowTestsBase { assertEquals(SCREEN_ORIENTATION_PORTRAIT, root.getOrientation()); } + @Test + public void testSetOrientation() { + final TestWindowContainer root = spy(new TestWindowContainerBuilder(mWm).build()); + final TestWindowContainer child = spy(root.addChildWindow()); + doReturn(true).when(root).handlesOrientationChangeFromDescendant(); + child.getWindowConfiguration().setWindowingMode(WINDOWING_MODE_FULLSCREEN); + child.setOrientation(SCREEN_ORIENTATION_PORTRAIT); + // The ancestor should decide whether to dispatch the configuration change. + verify(child, never()).onConfigurationChanged(any()); + + doReturn(false).when(root).handlesOrientationChangeFromDescendant(); + child.setOrientation(SCREEN_ORIENTATION_LANDSCAPE); + // The ancestor doesn't handle the request so the descendant applies the change directly. + verify(child).onConfigurationChanged(any()); + } + @Test public void testCompareTo() { final TestWindowContainerBuilder builder = new TestWindowContainerBuilder(mWm);