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

Commit b4a1b07e authored by Marzia Favaro's avatar Marzia Favaro Committed by Android (Google) Code Review
Browse files

Merge "Remove legacy edge extension code from SurfaceAnimationRunner" into main

parents db072d38 fa738e52
Loading
Loading
Loading
Loading
+3 −266
Original line number Diff line number Diff line
@@ -16,7 +16,6 @@

package com.android.server.wm;

import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
import static android.util.TimeUtils.NANOS_PER_MS;
import static android.view.Choreographer.CALLBACK_TRAVERSAL;
import static android.view.Choreographer.getSfInstance;
@@ -27,25 +26,13 @@ import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import android.annotation.Nullable;
import android.graphics.BitmapShader;
import android.graphics.Canvas;
import android.graphics.Insets;
import android.graphics.Paint;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.hardware.power.Boost;
import android.os.Handler;
import android.os.PowerManagerInternal;
import android.os.Trace;
import android.util.ArrayMap;
import android.util.Log;
import android.view.Choreographer;
import android.view.Surface;
import android.view.SurfaceControl;
import android.view.SurfaceControl.Transaction;
import android.view.animation.Animation;
import android.view.animation.Transformation;
import android.window.ScreenCapture;

import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
@@ -53,9 +40,6 @@ import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
import com.android.server.AnimationThread;
import com.android.server.wm.LocalAnimationAdapter.AnimationSpec;

import java.util.ArrayList;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.function.Supplier;

/**
@@ -73,12 +57,6 @@ class SurfaceAnimationRunner {
     */
    private final Object mCancelLock = new Object();

    /**
     * Lock for synchronizing {@link #mEdgeExtensions} to prevent race conditions when managing
     * created edge extension surfaces.
     */
    private final Object mEdgeExtensionLock = new Object();

    @VisibleForTesting
    Choreographer mChoreographer;

@@ -91,12 +69,6 @@ class SurfaceAnimationRunner {
    private final PowerManagerInternal mPowerManagerInternal;
    private boolean mApplyScheduled;

    // Executor to perform the edge extension.
    // With two threads because in practice we will want to extend two surfaces in one animation,
    // in which case we want to be able to parallelize those two extensions to cut down latency in
    // starting the animation.
    private final ExecutorService mEdgeExtensionExecutor = Executors.newFixedThreadPool(2);

    @GuardedBy("mLock")
    @VisibleForTesting
    final ArrayMap<SurfaceControl, RunningAnimation> mPendingAnimations = new ArrayMap<>();
@@ -112,11 +84,6 @@ class SurfaceAnimationRunner {
    @GuardedBy("mLock")
    private boolean mAnimationStartDeferred;

    // Mapping animation leashes to a list of edge extension surfaces associated with them
    @GuardedBy("mEdgeExtensionLock")
    private final ArrayMap<SurfaceControl, ArrayList<SurfaceControl>> mEdgeExtensions =
            new ArrayMap<>();

    /**
     * There should only ever be one instance of this class. Usual spot for it is with
     * {@link WindowManagerService}
@@ -175,65 +142,10 @@ class SurfaceAnimationRunner {
        synchronized (mLock) {
            final RunningAnimation runningAnim = new RunningAnimation(a, animationLeash,
                    finishCallback);
            boolean requiresEdgeExtension = requiresEdgeExtension(a);

            if (requiresEdgeExtension) {
                final ArrayList<SurfaceControl> extensionSurfaces = new ArrayList<>();
                synchronized (mEdgeExtensionLock) {
                    mEdgeExtensions.put(animationLeash, extensionSurfaces);
                }

                mPreProcessingAnimations.put(animationLeash, runningAnim);

                // We must wait for t to be committed since otherwise the leash doesn't have the
                // windows we want to screenshot and extend as children.
                t.addTransactionCommittedListener(mEdgeExtensionExecutor, () -> {
                    if (!animationLeash.isValid()) {
                        Log.e(TAG, "Animation leash is not valid");
                        synchronized (mEdgeExtensionLock) {
                            mEdgeExtensions.remove(animationLeash);
                        }
                        synchronized (mLock) {
                            mPreProcessingAnimations.remove(animationLeash);
                        }
                        return;
                    }
                    final WindowAnimationSpec animationSpec = a.asWindowAnimationSpec();

                    final Transaction edgeExtensionCreationTransaction = new Transaction();
                    edgeExtendWindow(animationLeash,
                            animationSpec.getRootTaskBounds(), animationSpec.getAnimation(),
                            edgeExtensionCreationTransaction);

                    synchronized (mLock) {
                        // only run if animation is not yet canceled by this point
                        if (mPreProcessingAnimations.get(animationLeash) == runningAnim) {
                            // In the case the animation is cancelled, edge extensions are removed
                            // onAnimationLeashLost which is called before onAnimationCancelled.
                            // So we need to check if the edge extensions have already been removed
                            // or not, and if so we don't want to apply the transaction.
                            synchronized (mEdgeExtensionLock) {
                                if (!mEdgeExtensions.isEmpty()) {
                                    edgeExtensionCreationTransaction.apply();
                                }
                            }

                            mPreProcessingAnimations.remove(animationLeash);
            mPendingAnimations.put(animationLeash, runningAnim);
            if (!mAnimationStartDeferred && mPreProcessingAnimations.isEmpty()) {
                mChoreographer.postFrameCallback(this::startAnimations);
            }
                        }
                    }
                });
            }

            if (!requiresEdgeExtension) {
                mPendingAnimations.put(animationLeash, runningAnim);
                if (!mAnimationStartDeferred && mPreProcessingAnimations.isEmpty()) {
                    mChoreographer.postFrameCallback(this::startAnimations);
                }
            }

            // Some animations (e.g. move animations) require the initial transform to be
            // applied immediately.
@@ -241,10 +153,6 @@ class SurfaceAnimationRunner {
        }
    }

    private boolean requiresEdgeExtension(AnimationSpec a) {
        return a.asWindowAnimationSpec() != null && a.asWindowAnimationSpec().hasExtension();
    }

    void onAnimationCancelled(SurfaceControl leash) {
        synchronized (mLock) {
            if (mPendingAnimations.containsKey(leash)) {
@@ -374,161 +282,6 @@ class SurfaceAnimationRunner {
        mApplyScheduled = false;
    }

    private void edgeExtendWindow(SurfaceControl leash, Rect bounds, Animation a,
            Transaction transaction) {
        final Transformation transformationAtStart = new Transformation();
        a.getTransformationAt(0, transformationAtStart);
        final Transformation transformationAtEnd = new Transformation();
        a.getTransformationAt(1, transformationAtEnd);

        // We want to create an extension surface that is the maximal size and the animation will
        // take care of cropping any part that overflows.
        final Insets maxExtensionInsets = Insets.min(
                transformationAtStart.getInsets(), transformationAtEnd.getInsets());

        final int targetSurfaceHeight = bounds.height();
        final int targetSurfaceWidth = bounds.width();

        if (maxExtensionInsets.left < 0) {
            final Rect edgeBounds = new Rect(bounds.left, bounds.top, bounds.left + 1,
                    bounds.bottom);
            final Rect extensionRect = new Rect(0, 0,
                    -maxExtensionInsets.left, targetSurfaceHeight);
            final int xPos = bounds.left + maxExtensionInsets.left;
            final int yPos = bounds.top;
            createExtensionSurface(leash, edgeBounds,
                    extensionRect, xPos, yPos, "Left Edge Extension", transaction);
        }

        if (maxExtensionInsets.top < 0) {
            final Rect edgeBounds = new Rect(bounds.left, bounds.top, targetSurfaceWidth,
                    bounds.top + 1);
            final Rect extensionRect = new Rect(0, 0,
                    targetSurfaceWidth, -maxExtensionInsets.top);
            final int xPos = bounds.left;
            final int yPos = bounds.top + maxExtensionInsets.top;
            createExtensionSurface(leash, edgeBounds,
                    extensionRect, xPos, yPos, "Top Edge Extension", transaction);
        }

        if (maxExtensionInsets.right < 0) {
            final Rect edgeBounds = new Rect(bounds.right - 1, bounds.top, bounds.right,
                    bounds.bottom);
            final Rect extensionRect = new Rect(0, 0,
                    -maxExtensionInsets.right, targetSurfaceHeight);
            final int xPos = bounds.right;
            final int yPos = bounds.top;
            createExtensionSurface(leash, edgeBounds,
                    extensionRect, xPos, yPos, "Right Edge Extension", transaction);
        }

        if (maxExtensionInsets.bottom < 0) {
            final Rect edgeBounds = new Rect(bounds.left, bounds.bottom - 1,
                    bounds.right, bounds.bottom);
            final Rect extensionRect = new Rect(0, 0,
                    targetSurfaceWidth, -maxExtensionInsets.bottom);
            final int xPos = bounds.left;
            final int yPos = bounds.bottom;
            createExtensionSurface(leash, edgeBounds,
                    extensionRect, xPos, yPos, "Bottom Edge Extension", transaction);
        }
    }

    private void createExtensionSurface(SurfaceControl leash, Rect edgeBounds,
            Rect extensionRect, int xPos, int yPos, String layerName,
            Transaction startTransaction) {
        Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "createExtensionSurface");
        doCreateExtensionSurface(leash, edgeBounds, extensionRect, xPos, yPos, layerName,
                startTransaction);
        Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
    }

    private void doCreateExtensionSurface(SurfaceControl leash, Rect edgeBounds,
            Rect extensionRect, int xPos, int yPos, String layerName,
            Transaction startTransaction) {
        ScreenCapture.LayerCaptureArgs captureArgs =
                new ScreenCapture.LayerCaptureArgs.Builder(leash /* surfaceToExtend */)
                        .setSourceCrop(edgeBounds)
                        .setFrameScale(1)
                        .setPixelFormat(PixelFormat.RGBA_8888)
                        .setChildrenOnly(true)
                        .setAllowProtected(true)
                        .setCaptureSecureLayers(true)
                        .build();
        final ScreenCapture.ScreenshotHardwareBuffer edgeBuffer =
                ScreenCapture.captureLayers(captureArgs);

        if (edgeBuffer == null) {
            // The leash we are trying to screenshot may have been removed by this point, which is
            // likely the reason for ending up with a null edgeBuffer, in which case we just want to
            // return and do nothing.
            Log.e(TAG, "Failed to create edge extension - edge buffer is null");
            return;
        }

        final SurfaceControl edgeExtensionLayer = new SurfaceControl.Builder()
                .setName(layerName)
                .setHidden(true)
                .setCallsite("DefaultTransitionHandler#startAnimation")
                .setOpaque(true)
                .setBufferSize(extensionRect.width(), extensionRect.height())
                .build();

        BitmapShader shader = new BitmapShader(edgeBuffer.asBitmap(),
                android.graphics.Shader.TileMode.CLAMP,
                android.graphics.Shader.TileMode.CLAMP);
        final Paint paint = new Paint();
        paint.setShader(shader);

        final Surface surface = new Surface(edgeExtensionLayer);
        Canvas c = surface.lockHardwareCanvas();
        c.drawRect(extensionRect, paint);
        surface.unlockCanvasAndPost(c);
        surface.release();

        synchronized (mEdgeExtensionLock) {
            if (!mEdgeExtensions.containsKey(leash)) {
                // The animation leash has already been removed, so we don't want to attach the
                // edgeExtension layer and should immediately remove it instead.
                startTransaction.remove(edgeExtensionLayer);
                return;
            }

            startTransaction.reparent(edgeExtensionLayer, leash);
            startTransaction.setLayer(edgeExtensionLayer, Integer.MIN_VALUE);
            startTransaction.setPosition(edgeExtensionLayer, xPos, yPos);
            startTransaction.setVisibility(edgeExtensionLayer, true);

            mEdgeExtensions.get(leash).add(edgeExtensionLayer);
        }
    }

    private float getScaleXForExtensionSurface(Rect edgeBounds, Rect extensionRect) {
        if (edgeBounds.width() == extensionRect.width()) {
            // Top or bottom edge extension, no need to scale the X axis of the extension surface.
            return 1;
        }
        if (edgeBounds.width() == 1) {
            // Left or right edge extension, scale the surface to be the extensionRect's width.
            return extensionRect.width();
        }

        throw new RuntimeException("Unexpected edgeBounds and extensionRect widths");
    }

    private float getScaleYForExtensionSurface(Rect edgeBounds, Rect extensionRect) {
        if (edgeBounds.height() == extensionRect.height()) {
            // Left or right edge extension, no need to scale the Y axis of the extension surface.
            return 1;
        }
        if (edgeBounds.height() == 1) {
            // Top or bottom edge extension, scale the surface to be the extensionRect's height.
            return extensionRect.height();
        }

        throw new RuntimeException("Unexpected edgeBounds and extensionRect heights");
    }

    private static final class RunningAnimation {
        final AnimationSpec mAnimSpec;
        final SurfaceControl mLeash;
@@ -545,22 +298,6 @@ class SurfaceAnimationRunner {
        }
    }

    protected void onAnimationLeashLost(SurfaceControl animationLeash,
            Transaction t) {
        synchronized (mEdgeExtensionLock) {
            if (!mEdgeExtensions.containsKey(animationLeash)) {
                return;
            }

            final ArrayList<SurfaceControl> edgeExtensions = mEdgeExtensions.get(animationLeash);
            for (int i = 0; i < edgeExtensions.size(); i++) {
                final SurfaceControl extension = edgeExtensions.get(i);
                t.remove(extension);
            }
            mEdgeExtensions.remove(animationLeash);
        }
    }

    @VisibleForTesting
    interface AnimatorFactory {
        ValueAnimator makeAnimator();
+0 −1
Original line number Diff line number Diff line
@@ -3067,7 +3067,6 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
    @Override
    public void onAnimationLeashLost(Transaction t) {
        mLastLayer = -1;
        mWmService.mSurfaceAnimationRunner.onAnimationLeashLost(mAnimationLeash, t);
        mAnimationLeash = null;
        mNeedsZBoost = false;
        reassignLayer(t);