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

Commit 8cb00aee authored by Vishnu Nair's avatar Vishnu Nair
Browse files

Prevent SurfaceView from drawing over parent surface insets

Parent SurfaceView to a bounds layer that enforces a crop preventing it
from drawing over the surface insets. The bounds layer crop is set to the
surface insets and updated when the parent surface size changes or the
parent surface insets change.

The SurfaceView has a z order relative to the ViewRootImpl surface so
that it can be behind or in front of the main content. If the ViewRootImpl
surface changes, then SurfaceView takes advantage of the
ViewRootImpl.SurfaceChangedCallback to update the relative z order.

Bug: 132205507
Test: go/wm-smoke
Test: test preserve surfaces code path with split screen and ensure relative z is preserved
Test: test SurfaceView apps, YouTube, maps & camera.
Test: try to repro maps pip issue described in bug
Change-Id: I0b2a3612b7e12ba66abb39b6bad4968442743cdd
parent c7487391
Loading
Loading
Loading
Loading
+44 −10
Original line number Original line Diff line number Diff line
@@ -38,6 +38,7 @@ import android.os.Looper;
import android.os.SystemClock;
import android.os.SystemClock;
import android.util.AttributeSet;
import android.util.AttributeSet;
import android.util.Log;
import android.util.Log;
import android.view.SurfaceControl.Transaction;


import com.android.internal.view.SurfaceCallbackHelper;
import com.android.internal.view.SurfaceCallbackHelper;


@@ -118,7 +119,7 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
    boolean mDrawFinished = false;
    boolean mDrawFinished = false;


    final Rect mScreenRect = new Rect();
    final Rect mScreenRect = new Rect();
    SurfaceSession mSurfaceSession;
    private final SurfaceSession mSurfaceSession = new SurfaceSession();


    SurfaceControl mSurfaceControl;
    SurfaceControl mSurfaceControl;
    // In the case of format changes we switch out the surface in-place
    // In the case of format changes we switch out the surface in-place
@@ -193,6 +194,7 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall


    private SurfaceControl.Transaction mRtTransaction = new SurfaceControl.Transaction();
    private SurfaceControl.Transaction mRtTransaction = new SurfaceControl.Transaction();
    private SurfaceControl.Transaction mTmpTransaction = new SurfaceControl.Transaction();
    private SurfaceControl.Transaction mTmpTransaction = new SurfaceControl.Transaction();
    private int mParentSurfaceGenerationId;


    public SurfaceView(Context context) {
    public SurfaceView(Context context) {
        this(context, null);
        this(context, null);
@@ -644,13 +646,12 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
        }
        }
    }
    }


    private void updateBackgroundVisibilityInTransaction(SurfaceControl viewRoot) {
    private void updateBackgroundVisibilityInTransaction() {
        if (mBackgroundControl == null) {
        if (mBackgroundControl == null) {
            return;
            return;
        }
        }
        if ((mSubLayer < 0) && ((mSurfaceFlags & SurfaceControl.OPAQUE) != 0)) {
        if ((mSubLayer < 0) && ((mSurfaceFlags & SurfaceControl.OPAQUE) != 0)) {
            mBackgroundControl.show();
            mBackgroundControl.show();
            mBackgroundControl.setRelativeLayer(viewRoot, Integer.MIN_VALUE);
        } else {
        } else {
            mBackgroundControl.hide();
            mBackgroundControl.hide();
        }
        }
@@ -742,11 +743,24 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
                mScreenRect.offset(surfaceInsets.left, surfaceInsets.top);
                mScreenRect.offset(surfaceInsets.left, surfaceInsets.top);


                if (creating) {
                if (creating) {
                    viewRoot.createBoundsSurface(mSubLayer);
                    mSurfaceSession = new SurfaceSession();
                    mDeferredDestroySurfaceControl = mSurfaceControl;
                    mDeferredDestroySurfaceControl = mSurfaceControl;


                    updateOpaqueFlag();
                    updateOpaqueFlag();
                    // SurfaceView hierarchy
                    // ViewRootImpl surface
                    //   - bounds layer (crops all child surfaces to parent surface insets)
                    //     - SurfaceView surface (drawn relative to ViewRootImpl surface)
                    //     - Background color layer (drawn behind all SurfaceView surfaces)
                    //
                    // The bounds layer is used to crop the surface view so it does not draw into
                    // the parent surface inset region. Since there can be multiple surface views
                    // below or above the parent surface, one option is to create multiple bounds
                    // layer for each z order. The other option, the one implement is to create
                    // a single bounds layer and set z order for each child surface relative to the
                    // parent surface.
                    // When creating the surface view, we parent it to the bounds layer and then
                    // set the relative z order. When the parent surface changes, we have to
                    // make sure to update the relative z via ViewRootImpl.SurfaceChangedCallback.
                    final String name = "SurfaceView - " + viewRoot.getTitle().toString();
                    final String name = "SurfaceView - " + viewRoot.getTitle().toString();


                    mSurfaceControl = new SurfaceControl.Builder(mSurfaceSession)
                    mSurfaceControl = new SurfaceControl.Builder(mSurfaceSession)
@@ -754,7 +768,7 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
                        .setOpaque((mSurfaceFlags & SurfaceControl.OPAQUE) != 0)
                        .setOpaque((mSurfaceFlags & SurfaceControl.OPAQUE) != 0)
                        .setBufferSize(mSurfaceWidth, mSurfaceHeight)
                        .setBufferSize(mSurfaceWidth, mSurfaceHeight)
                        .setFormat(mFormat)
                        .setFormat(mFormat)
                        .setParent(viewRoot.getSurfaceControl())
                        .setParent(viewRoot.getBoundsLayer())
                        .setFlags(mSurfaceFlags)
                        .setFlags(mSurfaceFlags)
                        .build();
                        .build();
                    mBackgroundControl = new SurfaceControl.Builder(mSurfaceSession)
                    mBackgroundControl = new SurfaceControl.Builder(mSurfaceSession)
@@ -779,14 +793,23 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall


                    SurfaceControl.openTransaction();
                    SurfaceControl.openTransaction();
                    try {
                    try {
                        mSurfaceControl.setLayer(mSubLayer);
                        // If we are creating the surface control or the parent surface has not
                        // changed, then set relative z. Otherwise allow the parent
                        // SurfaceChangedCallback to update the relative z. This is needed so that
                        // we do not change the relative z before the server is ready to swap the
                        // parent surface.
                        if (creating || (mParentSurfaceGenerationId
                                == viewRoot.mSurface.getGenerationId())) {
                            SurfaceControl.mergeToGlobalTransaction(updateRelativeZ());
                        }
                        mParentSurfaceGenerationId = viewRoot.mSurface.getGenerationId();


                        if (mViewVisibility) {
                        if (mViewVisibility) {
                            mSurfaceControl.show();
                            mSurfaceControl.show();
                        } else {
                        } else {
                            mSurfaceControl.hide();
                            mSurfaceControl.hide();
                        }
                        }
                        updateBackgroundVisibilityInTransaction(viewRoot.getSurfaceControl());
                        updateBackgroundVisibilityInTransaction();
                        if (mUseAlpha) {
                        if (mUseAlpha) {
                            mSurfaceControl.setAlpha(alpha);
                            mSurfaceControl.setAlpha(alpha);
                            mSurfaceAlpha = alpha;
                            mSurfaceAlpha = alpha;
@@ -1369,11 +1392,22 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
    }
    }


    /**
    /**
     * Called when a valid ViewRootImpl surface is replaced by another valid surface.
     * Called when a valid ViewRootImpl surface is replaced by another valid surface. In this
     * case update relative z to the new parent surface.
     * @hide
     * @hide
     */
     */
    @Override
    @Override
    public void surfaceReplaced(SurfaceControl.Transaction t) {
    public void surfaceReplaced(Transaction t) {
        if (mSurfaceControl != null && mBackgroundControl != null) {
            t.merge(updateRelativeZ());
        }
    }


    private Transaction updateRelativeZ() {
        Transaction t = new Transaction();
        SurfaceControl viewRoot = getViewRootImpl().getSurfaceControl();
        t.setRelativeLayer(mBackgroundControl, viewRoot, Integer.MIN_VALUE);
        t.setRelativeLayer(mSurfaceControl, viewRoot, mSubLayer);
        return t;
    }
    }
}
}
+34 −47
Original line number Original line Diff line number Diff line
@@ -484,15 +484,13 @@ public final class ViewRootImpl implements ViewParent,
     */
     */
    private final Transaction mSurfaceChangedTransaction = new Transaction();
    private final Transaction mSurfaceChangedTransaction = new Transaction();
    /**
    /**
     * Child surface of {@code mSurface} with the same bounds as its parent, and crop bounds
     * Child container layer of {@code mSurface} with the same bounds as its parent, and cropped to
     * are set to the parent's bounds adjusted for surface insets. This surface is created when
     * the surface insets. This surface is created only if a client requests it via {@link
     * {@link ViewRootImpl#createBoundsSurface(int)} is called.
     * #getBoundsLayer()}. By parenting to this bounds surface, child surfaces can ensure they do
     * By parenting to this bounds surface, child surfaces can ensure they do not draw into the
     * not draw into the surface inset region set by the parent window.
     * surface inset regions set by the parent window.
     */
     */
    public final Surface mBoundsSurface = new Surface();
    private SurfaceControl mBoundsLayer;
    private SurfaceSession mSurfaceSession;
    private final SurfaceSession mSurfaceSession = new SurfaceSession();
    private SurfaceControl mBoundsSurfaceControl;
    private final Transaction mTransaction = new Transaction();
    private final Transaction mTransaction = new Transaction();


    @UnsupportedAppUsage
    @UnsupportedAppUsage
@@ -1614,66 +1612,55 @@ public final class ViewRootImpl implements ViewParent,
        }
        }
    }
    }



    /**
    /**
     * Creates a surface as a child of {@code mSurface} with the same bounds as its parent and
     * @return child layer with the same bounds as its parent {@code mSurface} and cropped to the
     * crop bounds set to the parent's bounds adjusted for surface insets.
     * surface insets. If the layer does not exist, it is created.
     *
     *
     * @param zOrderLayer Z order relative to the parent surface.
     * <p>Parenting to this layer will ensure that its children are cropped by the view's surface
     * insets.
     */
     */
    public void createBoundsSurface(int zOrderLayer) {
    public SurfaceControl getBoundsLayer() {
        if (mSurfaceSession == null) {
        if (mBoundsLayer == null) {
            mSurfaceSession = new SurfaceSession();
            mBoundsLayer = new SurfaceControl.Builder(mSurfaceSession)
        }
                    .setContainerLayer()
        if (mBoundsSurfaceControl != null && mBoundsSurface.isValid()) {
            return; // surface control for bounds surface already exists.
        }

        mBoundsSurfaceControl = new SurfaceControl.Builder(mSurfaceSession)
                    .setName("Bounds for - " + getTitle().toString())
                    .setName("Bounds for - " + getTitle().toString())
                    .setParent(mSurfaceControl)
                    .setParent(mSurfaceControl)
                    .build();
                    .build();

            setBoundsLayerCrop();
        setBoundsSurfaceCrop();
            mTransaction.show(mBoundsLayer).apply();
        mTransaction.setLayer(mBoundsSurfaceControl, zOrderLayer)
        }
                    .show(mBoundsSurfaceControl)
        return mBoundsLayer;
                    .apply();
        mBoundsSurface.copyFrom(mBoundsSurfaceControl);
    }
    }


    private void setBoundsSurfaceCrop() {
    private void setBoundsLayerCrop() {
        // mWinFrame is already adjusted for surface insets. So offset it and use it as
        // mWinFrame is already adjusted for surface insets. So offset it and use it as
        // the cropping bounds.
        // the cropping bounds.
        mTempBoundsRect.set(mWinFrame);
        mTempBoundsRect.set(mWinFrame);
        mTempBoundsRect.offsetTo(mWindowAttributes.surfaceInsets.left,
        mTempBoundsRect.offsetTo(mWindowAttributes.surfaceInsets.left,
                mWindowAttributes.surfaceInsets.top);
                mWindowAttributes.surfaceInsets.top);
        mTransaction.setWindowCrop(mBoundsSurfaceControl, mTempBoundsRect);
        mTransaction.setWindowCrop(mBoundsLayer, mTempBoundsRect);
    }
    }


    /**
    /**
     * Called after window layout to update the bounds surface. If the surface insets have
     * Called after window layout to update the bounds surface. If the surface insets have changed
     * changed or the surface has resized, update the bounds surface.
     * or the surface has resized, update the bounds surface.
     */
     */
    private void updateBoundsSurface() {
    private void updateBoundsLayer() {
        if (mBoundsSurfaceControl != null && mSurface.isValid()) {
        if (mBoundsLayer != null) {
            setBoundsSurfaceCrop();
            setBoundsLayerCrop();
            mTransaction.deferTransactionUntilSurface(mBoundsSurfaceControl,
            mTransaction.deferTransactionUntilSurface(mBoundsLayer,
                    mSurface, mSurface.getNextFrameNumber())
                    mSurface, mSurface.getNextFrameNumber())
                    .apply();
                    .apply();
        }
        }
    }
    }


    private void destroySurface() {
    private void destroySurface() {
        if (mBoundsLayer != null) {
            mBoundsLayer.release();
            mBoundsLayer = null;
        }
        mSurface.release();
        mSurface.release();
        mSurfaceControl.release();
        mSurfaceControl.release();

        mSurfaceSession = null;

        if (mBoundsSurfaceControl != null) {
            mTransaction.remove(mBoundsSurfaceControl).apply();
            mBoundsSurface.release();
            mBoundsSurfaceControl = null;
        }
    }
    }


    /**
    /**
@@ -2649,8 +2636,8 @@ public final class ViewRootImpl implements ViewParent,
            maybeHandleWindowMove(frame);
            maybeHandleWindowMove(frame);
        }
        }


        if (surfaceSizeChanged) {
        if (surfaceSizeChanged || surfaceReplaced || surfaceCreated || windowAttributesChanged) {
            updateBoundsSurface();
            updateBoundsLayer();
        }
        }


        final boolean didLayout = layoutRequested && (!mStopped || mReportNextDraw);
        final boolean didLayout = layoutRequested && (!mStopped || mReportNextDraw);
+9 −4
Original line number Original line Diff line number Diff line
@@ -329,10 +329,15 @@ class WindowStateAnimator {
            }
            }
            mDrawState = COMMIT_DRAW_PENDING;
            mDrawState = COMMIT_DRAW_PENDING;
            layoutNeeded = true;
            layoutNeeded = true;
        }

            if (postDrawTransaction != null) {
            if (postDrawTransaction != null) {
                mPostDrawTransaction.merge(postDrawTransaction);
                mPostDrawTransaction.merge(postDrawTransaction);
            }
            }
        } else if (postDrawTransaction != null) {
            // If draw state is not pending we may delay applying this transaction from the client,
            // so apply it now.
            postDrawTransaction.apply();
        }


        return layoutNeeded;
        return layoutNeeded;
    }
    }
@@ -1296,7 +1301,7 @@ class WindowStateAnimator {
        // if we are transparent.
        // if we are transparent.
        if (mPendingDestroySurface != null && mDestroyPreservedSurfaceUponRedraw) {
        if (mPendingDestroySurface != null && mDestroyPreservedSurfaceUponRedraw) {
            final SurfaceControl pendingSurfaceControl = mPendingDestroySurface.mSurfaceControl;
            final SurfaceControl pendingSurfaceControl = mPendingDestroySurface.mSurfaceControl;
            mPostDrawTransaction.hide(pendingSurfaceControl);
            mPostDrawTransaction.reparent(pendingSurfaceControl, null);
            mPostDrawTransaction.reparentChildren(pendingSurfaceControl,
            mPostDrawTransaction.reparentChildren(pendingSurfaceControl,
                    mSurfaceController.mSurfaceControl);
                    mSurfaceController.mSurfaceControl);
        }
        }