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

Commit d08dc911 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Prevent SurfaceView from drawing over parent surface insets"

parents e54818c0 f7645aa9
Loading
Loading
Loading
Loading
+25 −25
Original line number Diff line number Diff line
@@ -1020,7 +1020,7 @@ public abstract class WallpaperService extends Service {
                        mIsCreating = false;
                        mSurfaceCreated = true;
                        if (redrawNeeded) {
                            mSession.finishDrawing(mWindow);
                            mSession.finishDrawing(mWindow, null /* postDrawTransaction */);
                        }
                        mIWallpaperEngine.reportShown();
                    }
+17 −9
Original line number Diff line number Diff line
@@ -31,6 +31,7 @@ import android.view.WindowManager;
import android.view.InsetsState;
import android.view.Surface;
import android.view.SurfaceControl;
import android.view.Transaction;

import java.util.List;

@@ -147,8 +148,15 @@ interface IWindowSession {
     */
    void getDisplayFrame(IWindow window, out Rect outDisplayFrame);

    /**
     * Called when the client has finished drawing the surface, if needed.
     *
     * @param postDrawTransaction transaction filled by the client that can be
     * used to synchronize any post draw transactions with the server. Transaction
     * is null if there is no sync required.
     */
    @UnsupportedAppUsage
    void finishDrawing(IWindow window);
    void finishDrawing(IWindow window, in Transaction postDrawTransaction);

    @UnsupportedAppUsage
    void setInTouchMode(boolean showFocus);
+8 −0
Original line number Diff line number Diff line
@@ -2693,6 +2693,14 @@ public final class SurfaceControl implements Parcelable {
            return this;
        }

        /**
         * Writes the transaction to parcel, clearing the transaction as if it had been applied so
         * it can be used to store future transactions. It's the responsibility of the parcel
         * reader to apply the original transaction.
         *
         * @param dest parcel to write the transaction to
         * @param flags
         */
        @Override
        public void writeToParcel(@NonNull Parcel dest, @WriteFlags int flags) {
            if (mNativeObject == 0) {
+40 −14
Original line number Diff line number Diff line
@@ -98,7 +98,8 @@ import java.util.concurrent.locks.ReentrantLock;
 * artifacts may occur on previous versions of the platform when its window is
 * positioned asynchronously.</p>
 */
public class SurfaceView extends View implements ViewRootImpl.WindowStoppedCallback {
public class SurfaceView extends View
        implements ViewRootImpl.WindowStoppedCallback, ViewRootImpl.SurfaceChangedCallback {
    private static final String TAG = "SurfaceView";
    private static final boolean DEBUG = false;

@@ -120,7 +121,7 @@ public class SurfaceView extends View implements ViewRootImpl.WindowStoppedCallb
    boolean mDrawFinished = false;

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

    SurfaceControl mSurfaceControl;
    // In the case of format changes we switch out the surface in-place
@@ -266,11 +267,22 @@ public class SurfaceView extends View implements ViewRootImpl.WindowStoppedCallb
        updateSurface();
    }

    /** @hide */
    @Override
    public void surfaceChangedCallback(SurfaceControl.Transaction transaction) {
        if (getViewRootImpl() != null && mBackgroundControl != null && mSurfaceControl != null) {
            SurfaceControl sc = getViewRootImpl().getSurfaceControl();
            transaction.setRelativeLayer(mBackgroundControl, sc, Integer.MIN_VALUE);
            transaction.setRelativeLayer(mSurfaceControl, sc, mSubLayer);
        }
    }

    @Override
    protected void onAttachedToWindow() {
        super.onAttachedToWindow();

        getViewRootImpl().addWindowStoppedCallback(this);
        getViewRootImpl().addSurfaceChangedCallback(this);
        mWindowStopped = false;

        mViewVisibility = getVisibility() == VISIBLE;
@@ -356,6 +368,7 @@ public class SurfaceView extends View implements ViewRootImpl.WindowStoppedCallb
        // the SurfaceHolder forward, most live wallpapers do it.
        if (viewRoot != null) {
            viewRoot.removeWindowStoppedCallback(this);
            viewRoot.removeSurfaceChangedCallback(this);
        }

        mAttachedToWindow = false;
@@ -637,19 +650,32 @@ public class SurfaceView extends View implements ViewRootImpl.WindowStoppedCallb
                mScreenRect.offset(surfaceInsets.left, surfaceInsets.top);

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

                    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();

                    mSurfaceControl = new SurfaceControl.Builder(mSurfaceSession)
                    mSurfaceControl =
                            new SurfaceControl.Builder(mSurfaceSession)
                                    .setName(name)
                                    .setOpaque((mSurfaceFlags & SurfaceControl.OPAQUE) != 0)
                                    .setBufferSize(mSurfaceWidth, mSurfaceHeight)
                                    .setFormat(mFormat)
                        .setParent(viewRoot.getSurfaceControl())
                                    .setParent(viewRoot.getBoundsLayer())
                                    .setFlags(mSurfaceFlags)
                                    .build();
                    mBackgroundControl = new SurfaceControl.Builder(mSurfaceSession)
@@ -674,7 +700,7 @@ public class SurfaceView extends View implements ViewRootImpl.WindowStoppedCallb

                    SurfaceControl.openTransaction();
                    try {
                        mSurfaceControl.setLayer(mSubLayer);
                        mSurfaceControl.setRelativeLayer(viewRoot.getSurfaceControl(), mSubLayer);

                        if (mViewVisibility) {
                            mSurfaceControl.show();
+70 −45
Original line number Diff line number Diff line
@@ -68,6 +68,7 @@ import android.os.Build;
import android.os.Bundle;
import android.os.Debug;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.os.ParcelFileDescriptor;
@@ -476,17 +477,19 @@ public final class ViewRootImpl implements ViewParent,
    @UnsupportedAppUsage
    public final Surface mSurface = new Surface();
    private final SurfaceControl mSurfaceControl = new SurfaceControl();
    private IBinder mPreviousSurfaceControlHandle = null;

    private final Transaction mChangeSurfaceTransaction = new Transaction();
    private final SurfaceSession mSurfaceSession = new SurfaceSession();

    /**
     * Child surface of {@code mSurface} with the same bounds as its parent, and crop bounds
     * are set to the parent's bounds adjusted for surface insets. This surface is created when
     * {@link ViewRootImpl#createBoundsSurface(int)} is called.
     * By parenting to this bounds surface, child surfaces can ensure they do not draw into the
     * surface inset regions set by the parent window.
     * Child container layer of {@code mSurface} with the same bounds as its parent, and cropped to
     * the surface insets. This surface is created only if a client requests it via {@link
     * #getBoundsLayer()}. By parenting to this bounds surface, child surfaces can ensure they do
     * not draw into the surface inset regions set by the parent window.
     */
    public final Surface mBoundsSurface = new Surface();
    private SurfaceSession mSurfaceSession;
    private SurfaceControl mBoundsSurfaceControl;
    private SurfaceControl mBoundsLayer;

    private final Transaction mTransaction = new Transaction();

    @UnsupportedAppUsage
@@ -1576,65 +1579,76 @@ public final class ViewRootImpl implements ViewParent,
        }
    }

    /**
     * Creates a surface as a child of {@code mSurface} with the same bounds as its parent and
     * crop bounds set to the parent's bounds adjusted for surface insets.
     *
     * @param zOrderLayer Z order relative to the parent surface.
     */
    public void createBoundsSurface(int zOrderLayer) {
        if (mSurfaceSession == null) {
            mSurfaceSession = new SurfaceSession();
    /** Register callback to be notified when the ViewRootImpl surface changes. */
    interface SurfaceChangedCallback {
        void surfaceChangedCallback(SurfaceControl.Transaction transaction);
    }
        if (mBoundsSurfaceControl != null && mBoundsSurface.isValid()) {
            return; // surface control for bounds surface already exists.

    private final ArrayList<SurfaceChangedCallback> mSurfaceChangedCallbacks = new ArrayList<>();
    void addSurfaceChangedCallback(SurfaceChangedCallback c) {
        mSurfaceChangedCallbacks.add(c);
    }

        mBoundsSurfaceControl = new SurfaceControl.Builder(mSurfaceSession)
    void removeSurfaceChangedCallback(SurfaceChangedCallback c) {
        mSurfaceChangedCallbacks.remove(c);
    }

    private void notifySurfaceChanged(SurfaceControl.Transaction transaction) {
        for (int i = 0; i < mSurfaceChangedCallbacks.size(); i++) {
            mSurfaceChangedCallbacks.get(i).surfaceChangedCallback(transaction);
        }
    }

     /**
     * Returns a child layer with the same bounds as its parent {@code mSurface} and cropped to the
     * surface insets. If the layer does not exist, it is created.
     *
     * <p>Parenting to this layer will ensure that its children are cropped by the view's surface
     * insets.
     */
    public SurfaceControl getBoundsLayer() {
        if (mBoundsLayer == null) {
            mBoundsLayer = new SurfaceControl.Builder(mSurfaceSession)
                .setContainerLayer()
                .setName("Bounds for - " + getTitle().toString())
                .setParent(mSurfaceControl)
                .build();

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

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

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

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

        mSurfaceSession = null;

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

    /**
@@ -2598,7 +2612,7 @@ public final class ViewRootImpl implements ViewParent,
        }

        if (surfaceSizeChanged) {
            updateBoundsSurface();
            updateBoundsLayer();
        }

        final boolean didLayout = layoutRequested && (!mStopped || mReportNextDraw);
@@ -3438,7 +3452,9 @@ public final class ViewRootImpl implements ViewParent,
    private void reportDrawFinished() {
        try {
            mDrawsNeededToReport = 0;
            mWindowSession.finishDrawing(mWindow);
            if (mSurfaceControl.isValid()) {
                mWindowSession.finishDrawing(mWindow, mChangeSurfaceTransaction);
            }
        } catch (RemoteException e) {
            // Have fun!
        }
@@ -7089,6 +7105,9 @@ public final class ViewRootImpl implements ViewParent,
            frameNumber = mSurface.getNextFrameNumber();
        }

        mPreviousSurfaceControlHandle = mSurfaceControl.isValid()
                ? mSurfaceControl.getHandle() : null;

        int relayoutResult = mWindowSession.relayout(mWindow, mSeq, params,
                (int) (mView.getMeasuredWidth() * appScale + 0.5f),
                (int) (mView.getMeasuredHeight() * appScale + 0.5f), viewVisibility,
@@ -7102,6 +7121,11 @@ public final class ViewRootImpl implements ViewParent,
            destroySurface();
        }

        if (mPreviousSurfaceControlHandle != null && mSurfaceControl.isValid()
                && mPreviousSurfaceControlHandle != mSurfaceControl.getHandle()) {
            notifySurfaceChanged(mChangeSurfaceTransaction);
        }

        mPendingAlwaysConsumeSystemBars =
                (relayoutResult & WindowManagerGlobal.RELAYOUT_RES_CONSUME_ALWAYS_SYSTEM_BARS) != 0;

@@ -7326,7 +7350,8 @@ public final class ViewRootImpl implements ViewParent,
                        try {
                            if ((relayoutWindow(mWindowAttributes, viewVisibility, false)
                                    & WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME) != 0) {
                                mWindowSession.finishDrawing(mWindow);
                                mWindowSession.finishDrawing(
                                        mWindow, null /* postDrawTransaction */);
                            }
                        } catch (RemoteException e) {
                        }
Loading