Set TaskFragment surface window crop

We don't want the child window to show outside of the TaskFragment.

Bug: 201261613
Bug: 196173550
Test: atest WmTests:TaskFragmentTest
Change-Id: I8350c5c57a79be9b2c7804847ebb1a5ffc4af015
This commit is contained in:
Chris Li
2021-09-28 17:46:41 +08:00
parent 4ffcb71834
commit d8dac94e0d
4 changed files with 163 additions and 7 deletions

View File

@@ -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;

View File

@@ -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() {

View File

@@ -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;
}

View File

@@ -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);
}
}