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

Commit fa738e52 authored by Marzia Favaro's avatar Marzia Favaro
Browse files

Remove legacy edge extension code from SurfaceAnimationRunner

Bug: 398776229
Bug: 365884835
Test: CtsWindowManagerDeviceAnimations:ActivityTransitionTests
Test: SurfaceAnimationRunnerTest
Test: AnimatingActivityRegistryTest PendingRemoteAnimationRegistryTest
Flag: EXEMPT removing unused code
Change-Id: I06cc92f398005d6cd6a6037b079a04f61deb2676
parent b072c6bd
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
@@ -3537,7 +3537,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);