From 68d02e9a25f7380a849fba1dae606a71839843c4 Mon Sep 17 00:00:00 2001 From: Charles Chen Date: Thu, 23 Apr 2020 11:41:18 +0800 Subject: [PATCH 1/2] Add DisplayArea support for WM#getMaximumWindowMetrics Set max bounds whenenver bounds change in DisplayContent and DisplayArea Bug: 151414021 Test: atest ConfigurationContainerTests#testSetMaxBoundsByHierarchy Test: atest ConfigurationContainerTests#testSetBoundsNotOverrideMaxBounds Test: atest DisplayAreaTest#testSetMaxBounds Test: atest DisplayAreaOrganizerTest Change-Id: I88be4f165cf8958eec0128e196368a630b2492aa --- core/java/android/view/Display.java | 13 ++- core/java/android/view/WindowManager.java | 12 ++- core/java/android/view/WindowManagerImpl.java | 16 ++-- .../server/wm/ConfigurationContainer.java | 81 ++++++++++++++++--- .../com/android/server/wm/DisplayArea.java | 5 ++ .../com/android/server/wm/DisplayContent.java | 6 ++ .../wm/ConfigurationContainerTests.java | 55 +++++++++++++ .../android/server/wm/DisplayAreaTest.java | 40 ++++++++- .../server/wm/TaskPositionerTests.java | 1 - 9 files changed, 206 insertions(+), 23 deletions(-) diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java index 0cc469a2d5eb0..c4048e5a032de 100644 --- a/core/java/android/view/Display.java +++ b/core/java/android/view/Display.java @@ -26,6 +26,7 @@ import android.annotation.SuppressLint; import android.annotation.TestApi; import android.app.KeyguardManager; import android.compat.annotation.UnsupportedAppUsage; +import android.content.Context; import android.content.res.CompatibilityInfo; import android.content.res.Configuration; import android.content.res.Resources; @@ -1157,9 +1158,19 @@ public final class Display { *

* The real size may be smaller than the physical size of the screen when the * window manager is emulating a smaller display (using adb shell wm size). - *

+ *

+ * In general, {@link #getRealSize(Point)} and {@link WindowManager#getMaximumWindowMetrics()} + * report the same bounds except that certain areas of the display may not be available to + * windows created in the {@link WindowManager}'s {@link Context}. + * + * For example, imagine a device which has a multi-task mode that limits windows to half of the + * screen. In this case, {@link WindowManager#getMaximumWindowMetrics()} reports the + * bounds of the screen half where the window is located, while {@link #getRealSize(Point)} + * still reports the bounds of the whole display. * * @param outSize Set to the real size of the display. + * + * @see WindowManager#getMaximumWindowMetrics() */ public void getRealSize(Point outSize) { synchronized (this) { diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java index 795e8f304f71f..14cc7ae9261da 100644 --- a/core/java/android/view/WindowManager.java +++ b/core/java/android/view/WindowManager.java @@ -72,6 +72,7 @@ import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.content.pm.ActivityInfo; import android.graphics.PixelFormat; +import android.graphics.Point; import android.graphics.Rect; import android.graphics.Region; import android.os.IBinder; @@ -473,9 +474,18 @@ public interface WindowManager extends ViewManager { * * Note that this might still be smaller than the size of the physical display if certain areas * of the display are not available to windows created in this {@link Context}. + *

+ * For example, given that there's a device which have a multi-task mode to limit activities + * to a half screen. In this case, {@link #getMaximumWindowMetrics()} reports the bounds of + * the half screen which the activity is located, while {@link Display#getRealSize(Point)} still + * reports the bounds of the whole physical display. * - * @see #getMaximumWindowMetrics() + * Despite this, {@link #getMaximumWindowMetrics()} and {@link Display#getRealSize(Point)} + * reports the same bounds in general. + * + * @see #getCurrentWindowMetrics() * @see WindowMetrics + * @see Display#getRealSize(Point) */ default @NonNull WindowMetrics getMaximumWindowMetrics() { throw new UnsupportedOperationException(); diff --git a/core/java/android/view/WindowManagerImpl.java b/core/java/android/view/WindowManagerImpl.java index 28a18da37b3e1..51b040506585a 100644 --- a/core/java/android/view/WindowManagerImpl.java +++ b/core/java/android/view/WindowManagerImpl.java @@ -29,7 +29,6 @@ import android.app.ResourcesManager; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.graphics.Insets; -import android.graphics.Point; import android.graphics.Rect; import android.graphics.Region; import android.os.Bundle; @@ -233,17 +232,16 @@ public final class WindowManagerImpl implements WindowManager { @Override public WindowMetrics getMaximumWindowMetrics() { - final Rect maxBounds = getMaximumBounds(); + final Context context = mParentWindow != null ? mParentWindow.getContext() : mContext; + final Rect maxBounds = getMaximumBounds(context); + return new WindowMetrics(maxBounds, computeWindowInsets(maxBounds)); } - private Rect getMaximumBounds() { - // TODO(b/128338354): Current maximum bound is display size, but it should be displayArea - // bound after displayArea feature is finished. - final Display display = mContext.getDisplayNoVerify(); - final Point displaySize = new Point(); - display.getRealSize(displaySize); - return new Rect(0, 0, displaySize.x, displaySize.y); + private static Rect getMaximumBounds(Context context) { + synchronized (ResourcesManager.getInstance()) { + return context.getResources().getConfiguration().windowConfiguration.getMaxBounds(); + } } // TODO(b/150095967): Set window type to LayoutParams diff --git a/services/core/java/com/android/server/wm/ConfigurationContainer.java b/services/core/java/com/android/server/wm/ConfigurationContainer.java index 98f57c5c31da0..c6cf68d0c547c 100644 --- a/services/core/java/com/android/server/wm/ConfigurationContainer.java +++ b/services/core/java/com/android/server/wm/ConfigurationContainer.java @@ -93,9 +93,17 @@ public abstract class ConfigurationContainer { private final Rect mTmpRect = new Rect(); static final int BOUNDS_CHANGE_NONE = 0; - // Return value from {@link setBounds} indicating the position of the override bounds changed. + + /** + * Return value from {@link #setBounds(Rect)} indicating the position of the override bounds + * changed. + */ static final int BOUNDS_CHANGE_POSITION = 1; - // Return value from {@link setBounds} indicating the size of the override bounds changed. + + /** + * Return value from {@link #setBounds(Rect)} indicating the size of the override bounds + * changed. + */ static final int BOUNDS_CHANGE_SIZE = 1 << 1; /** @@ -226,6 +234,11 @@ public abstract class ConfigurationContainer { return equivalentBounds(getRequestedOverrideBounds(), bounds); } + /** Similar to {@link #equivalentRequestedOverrideBounds(Rect)}, but compares max bounds. */ + public boolean equivalentRequestedOverrideMaxBounds(Rect bounds) { + return equivalentBounds(getRequestedOverrideMaxBounds(), bounds); + } + /** * Returns whether the two bounds are equal to each other or are a combination of null or empty. */ @@ -238,7 +251,6 @@ public abstract class ConfigurationContainer { /** * Returns the effective bounds of this container, inheriting the first non-empty bounds set in * its ancestral hierarchy, including itself. - * @return */ public Rect getBounds() { mReturnBounds.set(getConfiguration().windowConfiguration.getBounds()); @@ -249,6 +261,12 @@ public abstract class ConfigurationContainer { outBounds.set(getBounds()); } + /** Similar to {@link #getBounds()}, but reports the max bounds. */ + public Rect getMaxBounds() { + mReturnBounds.set(getConfiguration().windowConfiguration.getMaxBounds()); + return mReturnBounds; + } + /** * Sets {@code out} to the top-left corner of the bounds as returned by {@link #getBounds()}. */ @@ -273,6 +291,13 @@ public abstract class ConfigurationContainer { return mReturnBounds; } + /** Similar to {@link #getRequestedOverrideBounds()}, but returns the max bounds. */ + public Rect getRequestedOverrideMaxBounds() { + mReturnBounds.set(getRequestedOverrideConfiguration().windowConfiguration.getMaxBounds()); + + return mReturnBounds; + } + /** * Returns {@code true} if the {@link WindowConfiguration} in the requested override * {@link Configuration} specifies bounds. @@ -283,7 +308,7 @@ public abstract class ConfigurationContainer { /** * Sets the passed in {@link Rect} to the current bounds. - * @see {@link #getRequestedOverrideBounds()}. + * @see #getRequestedOverrideBounds() */ public void getRequestedOverrideBounds(Rect outBounds) { outBounds.set(getRequestedOverrideBounds()); @@ -295,19 +320,25 @@ public abstract class ConfigurationContainer { * {@link #getRequestedOverrideBounds()}. If * an empty {@link Rect} or null is specified, this container will be considered to match its * parent bounds {@see #matchParentBounds} and will inherit bounds from its parent. + * * @param bounds The bounds defining the container size. + * * @return a bitmask representing the types of changes made to the bounds. */ public int setBounds(Rect bounds) { int boundsChange = diffRequestedOverrideBounds(bounds); + final boolean overrideMaxBounds = providesMaxBounds() + && diffRequestedOverrideMaxBounds(bounds) != BOUNDS_CHANGE_NONE; - if (boundsChange == BOUNDS_CHANGE_NONE) { + if (boundsChange == BOUNDS_CHANGE_NONE && !overrideMaxBounds) { return boundsChange; } - mRequestsTmpConfig.setTo(getRequestedOverrideConfiguration()); mRequestsTmpConfig.windowConfiguration.setBounds(bounds); + if (overrideMaxBounds) { + mRequestsTmpConfig.windowConfiguration.setMaxBounds(bounds); + } onRequestedOverrideConfigurationChanged(mRequestsTmpConfig); return boundsChange; @@ -318,6 +349,40 @@ public abstract class ConfigurationContainer { return setBounds(mTmpRect); } + /** + * Returns {@code true} if this {@link ConfigurationContainer} provides the maximum bounds to + * its child {@link ConfigurationContainer}s. Returns {@code false}, otherwise. + *

+ * The maximum bounds is how large a window can be expanded. Currently only + * {@link DisplayContent} and {@link DisplayArea} effect this property. + *

+ */ + protected boolean providesMaxBounds() { + return false; + } + + int diffRequestedOverrideMaxBounds(Rect bounds) { + if (equivalentRequestedOverrideMaxBounds(bounds)) { + return BOUNDS_CHANGE_NONE; + } + + int boundsChange = BOUNDS_CHANGE_NONE; + + final Rect existingBounds = getRequestedOverrideMaxBounds(); + + if (bounds == null || existingBounds.left != bounds.left + || existingBounds.top != bounds.top) { + boundsChange |= BOUNDS_CHANGE_POSITION; + } + + if (bounds == null || existingBounds.width() != bounds.width() + || existingBounds.height() != bounds.height()) { + boundsChange |= BOUNDS_CHANGE_SIZE; + } + + return boundsChange; + } + int diffRequestedOverrideBounds(Rect bounds) { if (equivalentRequestedOverrideBounds(bounds)) { return BOUNDS_CHANGE_NONE; @@ -340,10 +405,6 @@ public abstract class ConfigurationContainer { return boundsChange; } - boolean hasOverrideConfiguration() { - return mHasOverrideConfiguration; - } - public WindowConfiguration getWindowConfiguration() { return mFullConfiguration.windowConfiguration; } diff --git a/services/core/java/com/android/server/wm/DisplayArea.java b/services/core/java/com/android/server/wm/DisplayArea.java index fbb2fcb15aee3..514581ad6c0b1 100644 --- a/services/core/java/com/android/server/wm/DisplayArea.java +++ b/services/core/java/com/android/server/wm/DisplayArea.java @@ -349,6 +349,11 @@ public class DisplayArea extends WindowContainer { return info; } + @Override + public boolean providesMaxBounds() { + return true; + } + /** * DisplayArea that contains WindowTokens, and orders them according to their type. */ diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index de91af752512d..67a21d4f617da 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -1901,6 +1901,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp final DisplayInfo displayInfo = updateDisplayAndOrientation(config.uiMode, config); calculateBounds(displayInfo, mTmpBounds); config.windowConfiguration.setBounds(mTmpBounds); + config.windowConfiguration.setMaxBounds(mTmpBounds); config.windowConfiguration.setWindowingMode(getWindowingMode()); config.windowConfiguration.setDisplayWindowingMode(getWindowingMode()); @@ -5420,6 +5421,11 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp }); } + @Override + public boolean providesMaxBounds() { + return true; + } + /** The entry for proceeding to handle {@link #mFixedRotationLaunchingApp}. */ class FixedRotationTransitionListener extends WindowManagerInternal.AppTransitionListener { diff --git a/services/tests/wmtests/src/com/android/server/wm/ConfigurationContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/ConfigurationContainerTests.java index bcd9371510464..5828d02948a14 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ConfigurationContainerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ConfigurationContainerTests.java @@ -33,6 +33,7 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import android.content.res.Configuration; +import android.graphics.Rect; import android.platform.test.annotations.Presubmit; import androidx.test.filters.SmallTest; @@ -324,6 +325,47 @@ public class ConfigurationContainerTests { assertEquals(100, listener.mOverrideConfiguration.smallestScreenWidthDp); } + @Test + public void testSetMaxBoundsByHierarchy() { + final TestConfigurationContainer root = + new TestConfigurationContainer(true /* providesMaxBounds */); + final Rect bounds = new Rect(0, 0, 10, 10); + final TestConfigurationContainer child = new TestConfigurationContainer(); + root.addChild(child); + + root.setBounds(bounds); + + assertEquals(bounds, root.getBounds()); + assertEquals(bounds, root.getConfiguration().windowConfiguration.getBounds()); + assertEquals(bounds, child.getBounds()); + assertEquals(bounds, child.getConfiguration().windowConfiguration.getBounds()); + + assertEquals(bounds, root.getMaxBounds()); + assertEquals(bounds, root.getConfiguration().windowConfiguration.getMaxBounds()); + assertEquals(bounds, child.getMaxBounds()); + assertEquals(bounds, child.getConfiguration().windowConfiguration.getMaxBounds()); + } + + @Test + public void testSetBoundsNotOverrideMaxBounds() { + final TestConfigurationContainer root = new TestConfigurationContainer(); + final Rect bounds = new Rect(0, 0, 10, 10); + final TestConfigurationContainer child = new TestConfigurationContainer(); + root.addChild(child); + + root.setBounds(bounds); + + assertEquals(bounds, root.getBounds()); + assertEquals(bounds, root.getConfiguration().windowConfiguration.getBounds()); + assertEquals(bounds, child.getBounds()); + assertEquals(bounds, child.getConfiguration().windowConfiguration.getBounds()); + + assertTrue(root.getMaxBounds().isEmpty()); + assertTrue(root.getConfiguration().windowConfiguration.getMaxBounds().isEmpty()); + assertTrue(child.getMaxBounds().isEmpty()); + assertTrue(child.getConfiguration().windowConfiguration.getMaxBounds().isEmpty()); + } + /** * Contains minimal implementation of {@link ConfigurationContainer}'s abstract behavior needed * for testing. @@ -333,6 +375,14 @@ public class ConfigurationContainerTests { private List mChildren = new ArrayList<>(); private TestConfigurationContainer mParent; + private boolean mProvidesMaxBounds = false; + + TestConfigurationContainer() {} + + TestConfigurationContainer(boolean providesMaxBounds) { + mProvidesMaxBounds = providesMaxBounds; + } + TestConfigurationContainer addChild(TestConfigurationContainer childContainer) { final ConfigurationContainer oldParent = childContainer.getParent(); childContainer.mParent = this; @@ -369,6 +419,11 @@ public class ConfigurationContainerTests { protected ConfigurationContainer getParent() { return mParent; } + + @Override + public boolean providesMaxBounds() { + return mProvidesMaxBounds; + } } /** diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java index c8ed87db9d3eb..e17601e99010c 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java @@ -16,6 +16,7 @@ package com.android.server.wm; +import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; import static android.view.WindowManager.LayoutParams.TYPE_PRESENTATION; import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER; @@ -40,8 +41,10 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyZeroInteractions; +import android.graphics.Rect; import android.os.Binder; import android.platform.test.annotations.Presubmit; +import android.view.SurfaceControl; import com.google.android.collect.Lists; @@ -63,7 +66,6 @@ import java.util.function.Function; */ @Presubmit public class DisplayAreaTest { - @Rule public SystemServicesTestRule mWmsRule = new SystemServicesTestRule(); @@ -379,6 +381,42 @@ public class DisplayAreaTest { assertThat(result).isEqualTo(tda1); } + @Test + public void testSetMaxBounds() { + final Rect parentBounds = new Rect(0, 0, 100, 100); + final Rect childBounds1 = new Rect(parentBounds.left, parentBounds.top, + parentBounds.right / 2, parentBounds.bottom); + final Rect childBounds2 = new Rect(parentBounds.right / 2, parentBounds.top, + parentBounds.right, parentBounds.bottom); + TestDisplayArea parentDa = new TestDisplayArea(mWms, parentBounds); + TestDisplayArea childDa1 = new TestDisplayArea(mWms, childBounds1); + TestDisplayArea childDa2 = new TestDisplayArea(mWms, childBounds2); + parentDa.addChild(childDa1, 0); + parentDa.addChild(childDa2, 1); + + assertEquals(parentBounds, parentDa.getMaxBounds()); + assertEquals(childBounds1, childDa1.getMaxBounds()); + assertEquals(childBounds2, childDa2.getMaxBounds()); + + final WindowToken windowToken = createWindowToken(TYPE_APPLICATION); + childDa1.addChild(windowToken, 0); + + assertEquals("DisplayArea's children must have the same max bounds as itself", + childBounds1, windowToken.getMaxBounds()); + } + + private static class TestDisplayArea extends DisplayArea { + private TestDisplayArea(WindowManagerService wms, Rect bounds) { + super(wms, ANY, "half display area"); + setBounds(bounds); + } + + @Override + SurfaceControl.Builder makeChildSurface(WindowContainer child) { + return new MockSurfaceControlBuilder(); + } + } + private WindowToken createWindowToken(int type) { return new WindowToken(mWmsRule.getWindowManagerService(), new Binder(), type, false /* persist */, null /* displayContent */, diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskPositionerTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskPositionerTests.java index 93dcc9103640e..5a869c44474af 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskPositionerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskPositionerTests.java @@ -37,7 +37,6 @@ import android.platform.test.annotations.Presubmit; import android.util.DisplayMetrics; import android.util.Log; -import androidx.test.filters.FlakyTest; import androidx.test.filters.SmallTest; import org.junit.After; From 5a24a59d0c74680b5ea8f864511f76eaf4149292 Mon Sep 17 00:00:00 2001 From: Charles Chen Date: Tue, 5 May 2020 18:30:19 +0800 Subject: [PATCH 2/2] Verify DisplayArea bounds in WindowMetricsTests Also add some test APIs to report max bounds value. Test: atest WindowMetricsTests Bug: 151414021 Change-Id: I6d0127925d51d2bd2b2879ce133504a46c66350e --- api/test-current.txt | 2 ++ core/java/android/app/WindowConfiguration.java | 7 ++----- core/proto/android/server/windowmanagerservice.proto | 1 + services/core/java/com/android/server/wm/DisplayArea.java | 6 ++++++ .../core/java/com/android/server/wm/TaskDisplayArea.java | 4 ++++ 5 files changed, 15 insertions(+), 5 deletions(-) diff --git a/api/test-current.txt b/api/test-current.txt index dc6626586efa8..74d602b95eba5 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -549,11 +549,13 @@ package android.app { method public int getActivityType(); method public android.graphics.Rect getAppBounds(); method public android.graphics.Rect getBounds(); + method @NonNull public android.graphics.Rect getMaxBounds(); method public int getRotation(); method public int getWindowingMode(); method public void setActivityType(int); method public void setAppBounds(android.graphics.Rect); method public void setBounds(android.graphics.Rect); + method public void setMaxBounds(@Nullable android.graphics.Rect); method public void setRotation(int); method public void setTo(android.app.WindowConfiguration); method public void setWindowingMode(int); diff --git a/core/java/android/app/WindowConfiguration.java b/core/java/android/app/WindowConfiguration.java index ec81ae3bc7c2b..79f05a3caa93f 100644 --- a/core/java/android/app/WindowConfiguration.java +++ b/core/java/android/app/WindowConfiguration.java @@ -310,7 +310,6 @@ public class WindowConfiguration implements Parcelable, Comparable extends WindowContainer { final long token = proto.start(fieldId); super.dumpDebug(proto, WINDOW_CONTAINER, logLevel); proto.write(NAME, mName); + proto.write(IS_TASK_DISPLAY_AREA, isTaskDisplayArea()); proto.end(token); } @@ -354,6 +356,10 @@ public class DisplayArea extends WindowContainer { return true; } + protected boolean isTaskDisplayArea() { + return false; + } + /** * DisplayArea that contains WindowTokens, and orders them according to their type. */ diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java index aba5b99f7481e..50cebc713a7ae 100644 --- a/services/core/java/com/android/server/wm/TaskDisplayArea.java +++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java @@ -1880,6 +1880,10 @@ final class TaskDisplayArea extends DisplayArea { return lastReparentedStack; } + @Override + protected boolean isTaskDisplayArea() { + return true; + } @Override void dump(PrintWriter pw, String prefix, boolean dumpAll) {