diff options
4 files changed, 163 insertions, 7 deletions
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index c5362d38c613..d89d212bab1f 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -480,7 +480,6 @@ class Task extends TaskFragment { private Dimmer mDimmer = new Dimmer(this); private final Rect mTmpDimBoundsRect = new Rect(); - private final Point mLastSurfaceSize = new Point(); /** @see #setCanAffectSystemUiFlags */ private boolean mCanAffectSystemUiFlags = true; diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java index fce279d8b805..2b5a8203d33b 100644 --- a/services/core/java/com/android/server/wm/TaskFragment.java +++ b/services/core/java/com/android/server/wm/TaskFragment.java @@ -240,6 +240,8 @@ class TaskFragment extends WindowContainer<WindowContainer> { */ private int mTaskFragmentOrganizerPid = ActivityRecord.INVALID_PID; + final Point mLastSurfaceSize = new Point(); + private final Rect mTmpInsets = new Rect(); private final Rect mTmpBounds = new Rect(); private final Rect mTmpFullBounds = new Rect(); @@ -1654,6 +1656,7 @@ class TaskFragment extends WindowContainer<WindowContainer> { } } + @Override void onChildPositionChanged(WindowContainer child) { super.onChildPositionChanged(child); @@ -2049,14 +2052,58 @@ class TaskFragment extends WindowContainer<WindowContainer> { if (shouldStartChangeTransition(mTmpPrevBounds)) { initializeChangeTransition(mTmpPrevBounds); } else if (mTaskFragmentOrganizer != null) { - // Update the surface position here instead of in the organizer so that we can make sure + // Update the surface here instead of in the organizer so that we can make sure // it can be synced with the surface freezer. - updateSurfacePosition(getSyncTransaction()); + final SurfaceControl.Transaction t = getSyncTransaction(); + updateSurfacePosition(t); + updateOrganizedTaskFragmentSurfaceSize(t, false /* forceUpdate */); } sendTaskFragmentInfoChanged(); } + /** Updates the surface size so that the sub windows cannot be shown out of bounds. */ + private void updateOrganizedTaskFragmentSurfaceSize(SurfaceControl.Transaction t, + boolean forceUpdate) { + if (mTaskFragmentOrganizer == null) { + // We only want to update for organized TaskFragment. Task will handle itself. + return; + } + if (mSurfaceControl == null || mSurfaceAnimator.hasLeash() || mSurfaceFreezer.hasLeash()) { + return; + } + + final Rect bounds = getBounds(); + final int width = bounds.width(); + final int height = bounds.height(); + if (!forceUpdate && width == mLastSurfaceSize.x && height == mLastSurfaceSize.y) { + return; + } + t.setWindowCrop(mSurfaceControl, width, height); + mLastSurfaceSize.set(width, height); + } + + @Override + public void onAnimationLeashCreated(SurfaceControl.Transaction t, SurfaceControl leash) { + super.onAnimationLeashCreated(t, leash); + // Reset surface bounds for animation. It will be taken care by the animation leash, and + // reset again onAnimationLeashLost. + if (mTaskFragmentOrganizer != null + && (mLastSurfaceSize.x != 0 || mLastSurfaceSize.y != 0)) { + t.setWindowCrop(mSurfaceControl, 0, 0); + mLastSurfaceSize.set(0, 0); + } + } + + @Override + public void onAnimationLeashLost(SurfaceControl.Transaction t) { + super.onAnimationLeashLost(t); + // Update the surface bounds after animation. + if (mTaskFragmentOrganizer != null) { + updateOrganizedTaskFragmentSurfaceSize(t, true /* forceUpdate */); + } + } + /** Whether we should prepare a transition for this {@link TaskFragment} bounds change. */ private boolean shouldStartChangeTransition(Rect startBounds) { if (mWmService.mDisableTransitionAnimation @@ -2075,9 +2122,14 @@ class TaskFragment extends WindowContainer<WindowContainer> { @Override void setSurfaceControl(SurfaceControl sc) { super.setSurfaceControl(sc); - // If the TaskFragmentOrganizer was set before we created the SurfaceControl, we need to - // emit the callbacks now. - sendTaskFragmentAppeared(); + if (mTaskFragmentOrganizer != null) { + final SurfaceControl.Transaction t = getSyncTransaction(); + updateSurfacePosition(t); + updateOrganizedTaskFragmentSurfaceSize(t, false /* forceUpdate */); + // If the TaskFragmentOrganizer was set before we created the SurfaceControl, we need to + // emit the callbacks now. + sendTaskFragmentAppeared(); + } } void sendTaskFragmentInfoChanged() { diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java index 2a8fa1086799..0bc3712d7946 100644 --- a/services/core/java/com/android/server/wm/WindowContainer.java +++ b/services/core/java/com/android/server/wm/WindowContainer.java @@ -3159,7 +3159,7 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< */ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PROTECTED) void updateSurfacePosition(Transaction t) { - if (mSurfaceControl == null || mSurfaceAnimator.hasLeash()) { + if (mSurfaceControl == null || mSurfaceAnimator.hasLeash() || mSurfaceFreezer.hasLeash()) { return; } diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java new file mode 100644 index 000000000000..cb209abf6aa9 --- /dev/null +++ b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java @@ -0,0 +1,105 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.wm; + +import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; + +import static org.mockito.Mockito.clearInvocations; + +import android.graphics.Rect; +import android.platform.test.annotations.Presubmit; +import android.view.SurfaceControl; +import android.window.ITaskFragmentOrganizer; +import android.window.TaskFragmentOrganizer; + +import androidx.test.filters.MediumTest; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +/** + * Test class for {@link TaskFragment}. + * + * Build/Install/Run: + * atest WmTests:TaskFragmentTest + */ +@MediumTest +@Presubmit +@RunWith(WindowTestRunner.class) +public class TaskFragmentTest extends WindowTestsBase { + + private TaskFragmentOrganizer mOrganizer; + private TaskFragment mTaskFragment; + private SurfaceControl mLeash; + @Mock + private SurfaceControl.Transaction mTransaction; + + @Before + public void setup() { + MockitoAnnotations.initMocks(this); + mOrganizer = new TaskFragmentOrganizer(Runnable::run); + final ITaskFragmentOrganizer iOrganizer = + ITaskFragmentOrganizer.Stub.asInterface(mOrganizer.getOrganizerToken().asBinder()); + mAtm.mWindowOrganizerController.mTaskFragmentOrganizerController + .registerOrganizer(iOrganizer); + mTaskFragment = new TaskFragmentBuilder(mAtm) + .setCreateParentTask() + .setOrganizer(mOrganizer) + .build(); + mLeash = mTaskFragment.getSurfaceControl(); + spyOn(mTaskFragment); + doReturn(mTransaction).when(mTaskFragment).getSyncTransaction(); + doReturn(mTransaction).when(mTaskFragment).getPendingTransaction(); + } + + @Test + public void testOnConfigurationChanged_updateSurface() { + final Rect bounds = new Rect(100, 100, 1100, 1100); + mTaskFragment.setBounds(bounds); + + verify(mTransaction).setPosition(mLeash, 100, 100); + verify(mTransaction).setWindowCrop(mLeash, 1000, 1000); + } + + @Test + public void testStartChangeTransition_resetSurface() { + final Rect startBounds = new Rect(0, 0, 1000, 1000); + final Rect endBounds = new Rect(500, 500, 1000, 1000); + mTaskFragment.setBounds(startBounds); + doReturn(true).when(mTaskFragment).isVisible(); + + clearInvocations(mTransaction); + mTaskFragment.setBounds(endBounds); + + // Surface reset when prepare transition. + verify(mTaskFragment).initializeChangeTransition(startBounds); + verify(mTransaction).setPosition(mLeash, 0, 0); + verify(mTransaction).setWindowCrop(mLeash, 0, 0); + + clearInvocations(mTransaction); + mTaskFragment.mSurfaceFreezer.unfreeze(mTransaction); + + // Update surface after animation. + verify(mTransaction).setPosition(mLeash, 500, 500); + verify(mTransaction).setWindowCrop(mLeash, 500, 500); + } +} |