Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c440b4e4 authored by Evan Rosky's avatar Evan Rosky
Browse files

Home tasks inherit split or fullscreen from parent.

Also, changes the "inheritance" of bounds-based configs so
that when the window bounds are overridden, the configs will
implicitly also be overridden (recalculated against display)
appBounds and screenW/H match the overridden bounds.

Bug: 154331729
Bug: 153662925
Test: enter split-screen, open ime in launcher. Updated some
      tests to check for this expectation.
Change-Id: I1e37d8d116fde90428036bc54a2a77b293cee338
parent 4695d593
Loading
Loading
Loading
Loading
+31 −21
Original line number Diff line number Diff line
@@ -2225,14 +2225,16 @@ class Task extends WindowContainer<WindowContainer> {
        }
        density *= DisplayMetrics.DENSITY_DEFAULT_SCALE;

        // If bounds have been overridden at this level, restrict config resources to these bounds
        // rather than the parent because the overridden bounds can be larger than the parent.
        boolean hasOverrideBounds = false;

        final Rect resolvedBounds = inOutConfig.windowConfiguration.getBounds();
        if (resolvedBounds == null) {
            mTmpFullBounds.setEmpty();
        if (resolvedBounds == null || resolvedBounds.isEmpty()) {
            mTmpFullBounds.set(parentConfig.windowConfiguration.getBounds());
        } else {
            mTmpFullBounds.set(resolvedBounds);
        }
        if (mTmpFullBounds.isEmpty()) {
            mTmpFullBounds.set(parentConfig.windowConfiguration.getBounds());
            hasOverrideBounds = true;
        }

        Rect outAppBounds = inOutConfig.windowConfiguration.getAppBounds();
@@ -2244,7 +2246,16 @@ class Task extends WindowContainer<WindowContainer> {
        // the out bounds doesn't need to be restricted by the parent.
        final boolean insideParentBounds = compatInsets == null;
        if (insideParentBounds && windowingMode != WINDOWING_MODE_FREEFORM) {
            final Rect parentAppBounds = parentConfig.windowConfiguration.getAppBounds();
            Rect parentAppBounds;
            if (hasOverrideBounds) {
                // Since we overrode the bounds, restrict appBounds to display non-decor rather
                // than parent. Otherwise, it won't match the overridden bounds.
                final TaskDisplayArea displayArea = getDisplayArea();
                parentAppBounds = displayArea != null
                        ? displayArea.getConfiguration().windowConfiguration.getAppBounds() : null;
            } else {
                parentAppBounds = parentConfig.windowConfiguration.getAppBounds();
            }
            if (parentAppBounds != null && !parentAppBounds.isEmpty()) {
                outAppBounds.intersect(parentAppBounds);
            }
@@ -2291,13 +2302,13 @@ class Task extends WindowContainer<WindowContainer> {

            if (inOutConfig.screenWidthDp == Configuration.SCREEN_WIDTH_DP_UNDEFINED) {
                final int overrideScreenWidthDp = (int) (mTmpStableBounds.width() / density);
                inOutConfig.screenWidthDp = insideParentBounds
                inOutConfig.screenWidthDp = (insideParentBounds && !hasOverrideBounds)
                        ? Math.min(overrideScreenWidthDp, parentConfig.screenWidthDp)
                        : overrideScreenWidthDp;
            }
            if (inOutConfig.screenHeightDp == Configuration.SCREEN_HEIGHT_DP_UNDEFINED) {
                final int overrideScreenHeightDp = (int) (mTmpStableBounds.height() / density);
                inOutConfig.screenHeightDp = insideParentBounds
                inOutConfig.screenHeightDp = (insideParentBounds && !hasOverrideBounds)
                        ? Math.min(overrideScreenHeightDp, parentConfig.screenHeightDp)
                        : overrideScreenHeightDp;
            }
@@ -2344,27 +2355,27 @@ class Task extends WindowContainer<WindowContainer> {
        mTmpBounds.set(getResolvedOverrideConfiguration().windowConfiguration.getBounds());
        super.resolveOverrideConfiguration(newParentConfig);

        // Resolve override windowing mode to fullscreen for home task (even on freeform
        // display), or split-screen-secondary if in split-screen mode.
        int windowingMode =
                getResolvedOverrideConfiguration().windowConfiguration.getWindowingMode();

        // Resolve override windowing mode to fullscreen for home task (even on freeform
        // display), or split-screen if in split-screen mode.
        if (getActivityType() == ACTIVITY_TYPE_HOME && windowingMode == WINDOWING_MODE_UNDEFINED) {
            final int parentWindowingMode = newParentConfig.windowConfiguration.getWindowingMode();
            windowingMode = parentWindowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY
                    ? WINDOWING_MODE_SPLIT_SCREEN_SECONDARY
                    : WINDOWING_MODE_FULLSCREEN;
            windowingMode = WindowConfiguration.isSplitScreenWindowingMode(parentWindowingMode)
                    ? parentWindowingMode : WINDOWING_MODE_FULLSCREEN;
            getResolvedOverrideConfiguration().windowConfiguration.setWindowingMode(windowingMode);
        }

        if (!isLeafTask()) {
            // Compute configuration overrides for tasks that created by organizer, so that
            // organizer can get the correct configuration from those tasks.
            if (mCreatedByOrganizer) {
                computeConfigResourceOverrides(getResolvedOverrideConfiguration(), newParentConfig);
        if (isLeafTask()) {
            resolveLeafOnlyOverrideConfigs(newParentConfig);
        }
            return;
        computeConfigResourceOverrides(getResolvedOverrideConfiguration(), newParentConfig);
    }

    void resolveLeafOnlyOverrideConfigs(Configuration newParentConfig) {
        int windowingMode =
                getResolvedOverrideConfiguration().windowConfiguration.getWindowingMode();
        if (windowingMode == WINDOWING_MODE_UNDEFINED) {
            windowingMode = newParentConfig.windowConfiguration.getWindowingMode();
        }
@@ -2404,7 +2415,6 @@ class Task extends WindowContainer<WindowContainer> {
                outOverrideBounds.offset(0, offsetTop);
            }
        }
        computeConfigResourceOverrides(getResolvedOverrideConfiguration(), newParentConfig);
    }

    /**
+9 −1
Original line number Diff line number Diff line
@@ -329,6 +329,7 @@ class ActivityTestsBase extends SystemServiceTestsBase {
        private boolean mCreateStack = true;

        private ActivityStack mStack;
        private TaskDisplayArea mTaskDisplayArea;

        TaskBuilder(ActivityStackSupervisor supervisor) {
            mSupervisor = supervisor;
@@ -378,9 +379,16 @@ class ActivityTestsBase extends SystemServiceTestsBase {
            return this;
        }

        TaskBuilder setDisplay(DisplayContent display) {
            mTaskDisplayArea = display.getDefaultTaskDisplayArea();
            return this;
        }

        Task build() {
            if (mStack == null && mCreateStack) {
                mStack = mSupervisor.mRootWindowContainer.getDefaultTaskDisplayArea().createStack(
                TaskDisplayArea displayArea = mTaskDisplayArea != null ? mTaskDisplayArea
                        : mSupervisor.mRootWindowContainer.getDefaultTaskDisplayArea();
                mStack = displayArea.createStack(
                        WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
                spyOn(mStack);
            }
+34 −24
Original line number Diff line number Diff line
@@ -54,10 +54,7 @@ import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.same;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;

import android.app.ActivityManager;
import android.app.TaskInfo;
@@ -75,12 +72,10 @@ import android.util.DisplayMetrics;
import android.util.Xml;
import android.view.DisplayInfo;

import androidx.test.filters.FlakyTest;
import androidx.test.filters.MediumTest;

import com.android.internal.app.IVoiceInteractor;
import com.android.server.wm.Task.TaskFactory;
import com.android.server.wm.utils.WmDisplayCutout;

import org.junit.Before;
import org.junit.Test;
@@ -368,25 +363,38 @@ public class TaskRecordTests extends ActivityTestsBase {

    @Test
    public void testComputeConfigResourceOverrides() {
        final Task task = new TaskBuilder(mSupervisor).build();
        final Rect fullScreenBounds = new Rect(0, 0, 1080, 1920);
        TestDisplayContent display = new TestDisplayContent.Builder(
                mService, fullScreenBounds.width(), fullScreenBounds.height()).build();
        final Task task = new TaskBuilder(mSupervisor).setDisplay(display).build();
        final Configuration inOutConfig = new Configuration();
        final Configuration parentConfig = new Configuration();
        final int longSide = 1200;
        final int shortSide = 600;
        final Rect parentBounds = new Rect(0, 0, 250, 500);
        parentConfig.windowConfiguration.setBounds(parentBounds);
        parentConfig.densityDpi = 400;
        parentConfig.screenHeightDp = 200; // 200 * 400 / 160 = 500px
        parentConfig.screenWidthDp = 100; // 100 * 400 / 160 = 250px
        parentConfig.screenHeightDp = (parentBounds.bottom * 160) / parentConfig.densityDpi; // 200
        parentConfig.screenWidthDp = (parentBounds.right * 160) / parentConfig.densityDpi; // 100
        parentConfig.windowConfiguration.setRotation(ROTATION_0);

        // Portrait bounds.
        inOutConfig.windowConfiguration.getBounds().set(0, 0, shortSide, longSide);
        // By default, the parent bounds should limit the existing input bounds.
        // By default, the input bounds will fill parent.
        task.computeConfigResourceOverrides(inOutConfig, parentConfig);

        assertEquals(parentConfig.screenHeightDp, inOutConfig.screenHeightDp);
        assertEquals(parentConfig.screenWidthDp, inOutConfig.screenWidthDp);
        assertEquals(Configuration.ORIENTATION_PORTRAIT, inOutConfig.orientation);

        // If bounds are overridden, config properties should be made to match. Surface hierarchy
        // will crop for policy.
        inOutConfig.setToDefaults();
        inOutConfig.windowConfiguration.getBounds().set(0, 0, shortSide, longSide);
        // By default, the parent bounds should limit the existing input bounds.
        task.computeConfigResourceOverrides(inOutConfig, parentConfig);

        assertEquals(longSide, inOutConfig.screenHeightDp * parentConfig.densityDpi / 160);
        assertEquals(shortSide, inOutConfig.screenWidthDp * parentConfig.densityDpi / 160);

        inOutConfig.setToDefaults();
        // Landscape bounds.
        inOutConfig.windowConfiguration.getBounds().set(0, 0, longSide, shortSide);
@@ -394,21 +402,17 @@ public class TaskRecordTests extends ActivityTestsBase {
        // Setup the display with a top stable inset. The later assertion will ensure the inset is
        // excluded from screenHeightDp.
        final int statusBarHeight = 100;
        final DisplayContent displayContent = task.mDisplayContent;
        final DisplayPolicy policy = mock(DisplayPolicy.class);
        final DisplayPolicy policy = display.getDisplayPolicy();
        doAnswer(invocationOnMock -> {
            final Rect insets = invocationOnMock.<Rect>getArgument(0);
            insets.top = statusBarHeight;
            return null;
        }).when(policy).convertNonDecorInsetsToStableInsets(any(), eq(ROTATION_0));
        doReturn(policy).when(displayContent).getDisplayPolicy();
        doReturn(mock(WmDisplayCutout.class)).when(displayContent)
                .calculateDisplayCutoutForRotation(anyInt());

        // Without limiting to be inside the parent bounds, the out screen size should keep relative
        // to the input bounds.
        final ActivityRecord.CompatDisplayInsets compatIntsets =
                new ActivityRecord.CompatDisplayInsets(displayContent, task);
                new ActivityRecord.CompatDisplayInsets(display, task);
        task.computeConfigResourceOverrides(inOutConfig, parentConfig, compatIntsets);

        assertEquals((shortSide - statusBarHeight) * DENSITY_DEFAULT / parentConfig.densityDpi,
@@ -454,7 +458,6 @@ public class TaskRecordTests extends ActivityTestsBase {
        parentConfig.screenWidthDp = 100; // 100 * 400 / 160 = 250px
        parentConfig.windowConfiguration.setRotation(ROTATION_0);

        final float density = 2.5f; // densityDpi / DENSITY_DEFAULT_SCALE = 400 / 160.0f
        final int longSideDp = 480; // longSide / density = 1200 / 400 * 160
        final int shortSideDp = 240; // shortSide / density = 600 / 400 * 160
        final int screenLayout = parentConfig.screenLayout
@@ -463,31 +466,38 @@ public class TaskRecordTests extends ActivityTestsBase {
                Configuration.reduceScreenLayout(screenLayout, longSideDp, shortSideDp);

        // Portrait bounds overlapping with navigation bar, without insets.
        inOutConfig.windowConfiguration.getBounds().set(0,
        final Rect freeformBounds = new Rect(0,
                displayHeight - 10 - longSide,
                shortSide,
                displayHeight - 10);
        inOutConfig.windowConfiguration.setBounds(freeformBounds);
        // Set to freeform mode to verify bug fix.
        inOutConfig.windowConfiguration.setWindowingMode(WINDOWING_MODE_FREEFORM);

        task.computeConfigResourceOverrides(inOutConfig, parentConfig);

        assertEquals(parentConfig.screenWidthDp, inOutConfig.screenWidthDp);
        assertEquals(parentConfig.screenHeightDp, inOutConfig.screenHeightDp);
        // screenW/H should not be effected by parent since overridden and freeform
        assertEquals(freeformBounds.width() * 160 / parentConfig.densityDpi,
                inOutConfig.screenWidthDp);
        assertEquals(freeformBounds.height() * 160 / parentConfig.densityDpi,
                inOutConfig.screenHeightDp);
        assertEquals(reducedScreenLayout, inOutConfig.screenLayout);

        inOutConfig.setToDefaults();
        // Landscape bounds overlapping with navigtion bar, without insets.
        inOutConfig.windowConfiguration.getBounds().set(0,
        freeformBounds.set(0,
                displayHeight - 10 - shortSide,
                longSide,
                displayHeight - 10);
        inOutConfig.windowConfiguration.setBounds(freeformBounds);
        inOutConfig.windowConfiguration.setWindowingMode(WINDOWING_MODE_FREEFORM);

        task.computeConfigResourceOverrides(inOutConfig, parentConfig);

        assertEquals(parentConfig.screenWidthDp, inOutConfig.screenWidthDp);
        assertEquals(parentConfig.screenHeightDp, inOutConfig.screenHeightDp);
        assertEquals(freeformBounds.width() * 160 / parentConfig.densityDpi,
                inOutConfig.screenWidthDp);
        assertEquals(freeformBounds.height() * 160 / parentConfig.densityDpi,
                inOutConfig.screenHeightDp);
        assertEquals(reducedScreenLayout, inOutConfig.screenLayout);
    }

+3 −6
Original line number Diff line number Diff line
@@ -44,7 +44,6 @@ import static com.android.server.wm.WindowContainer.POSITION_TOP;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
@@ -407,22 +406,20 @@ public class WindowOrganizerTests extends WindowTestsBase {
                .setWindowingMode(WINDOWING_MODE_FREEFORM).build();
        final Task task = stack.getTopMostTask();
        WindowContainerTransaction t = new WindowContainerTransaction();
        t.setBounds(task.mRemoteToken.toWindowContainerToken(), new Rect(10, 10, 100, 100));
        mWm.mAtmService.mWindowOrganizerController.applyTransaction(t);
        final int origScreenWDp = task.getConfiguration().screenHeightDp;
        final int origScreenHDp = task.getConfiguration().screenHeightDp;
        t = new WindowContainerTransaction();
        // verify that setting config overrides on parent restricts children.
        t.setScreenSizeDp(stack.mRemoteToken
                .toWindowContainerToken(), origScreenWDp, origScreenHDp);
        t.setBounds(task.mRemoteToken.toWindowContainerToken(), new Rect(10, 10, 150, 200));
                .toWindowContainerToken(), origScreenWDp, origScreenHDp / 2);
        mWm.mAtmService.mWindowOrganizerController.applyTransaction(t);
        assertEquals(origScreenHDp, task.getConfiguration().screenHeightDp);
        assertEquals(origScreenHDp / 2, task.getConfiguration().screenHeightDp);
        t = new WindowContainerTransaction();
        t.setScreenSizeDp(stack.mRemoteToken.toWindowContainerToken(), SCREEN_WIDTH_DP_UNDEFINED,
                SCREEN_HEIGHT_DP_UNDEFINED);
        mWm.mAtmService.mWindowOrganizerController.applyTransaction(t);
        assertNotEquals(origScreenHDp, task.getConfiguration().screenHeightDp);
        assertEquals(origScreenHDp, task.getConfiguration().screenHeightDp);
    }

    @Test