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

Commit 5570147a authored by Winson Chung's avatar Winson Chung
Browse files

Ensure that task org updates all happen off the main thread

- Move the animator to be called on the update thread
- Move the calls on task org to update on that thread as well
- Cache the leash and token to ensure we don't make binder calls to fetch
  the leash on each frame of the animation
- Don't align with SF vsync now that we're driving the surface animations

Bug: 150810666
Test: Enter PIP, move it around
Test: atest PipAnimationControllerTest
Change-Id: Id05980529681f892638f52f492262fde246cac20
parent 721de235
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -64,7 +64,7 @@ public class InputConsumerController {
    private final class InputEventReceiver extends BatchedInputEventReceiver {

        public InputEventReceiver(InputChannel inputChannel, Looper looper) {
            super(inputChannel, looper, Choreographer.getSfInstance());
            super(inputChannel, looper, Choreographer.getInstance());
        }

        @Override
+29 −38
Original line number Diff line number Diff line
@@ -19,11 +19,8 @@ package com.android.systemui.pip;
import android.animation.Animator;
import android.animation.ValueAnimator;
import android.annotation.IntDef;
import android.annotation.MainThread;
import android.content.Context;
import android.graphics.Rect;
import android.os.RemoteException;
import android.view.IWindowContainer;
import android.view.SurfaceControl;
import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
@@ -61,31 +58,30 @@ public class PipAnimationController {
                com.android.internal.R.interpolator.fast_out_slow_in);
    }

    @MainThread
    PipTransitionAnimator getAnimator(IWindowContainer wc, boolean scheduleFinishPip,
    PipTransitionAnimator getAnimator(SurfaceControl leash, boolean scheduleFinishPip,
            Rect destinationBounds, float alphaStart, float alphaEnd) {
        if (mCurrentAnimator == null) {
            mCurrentAnimator = setupPipTransitionAnimator(
                    PipTransitionAnimator.ofAlpha(wc, scheduleFinishPip,
                            destinationBounds, alphaStart, alphaEnd));
                    PipTransitionAnimator.ofAlpha(leash, scheduleFinishPip, destinationBounds,
                            alphaStart, alphaEnd));
        } else if (mCurrentAnimator.getAnimationType() == ANIM_TYPE_ALPHA
                && mCurrentAnimator.isRunning()) {
            mCurrentAnimator.updateEndValue(alphaEnd);
        } else {
            mCurrentAnimator.cancel();
            mCurrentAnimator = setupPipTransitionAnimator(
                    PipTransitionAnimator.ofAlpha(wc, scheduleFinishPip,
                            destinationBounds, alphaStart, alphaEnd));
                    PipTransitionAnimator.ofAlpha(leash, scheduleFinishPip, destinationBounds,
                            alphaStart, alphaEnd));
        }
        return mCurrentAnimator;
    }

    @MainThread
    PipTransitionAnimator getAnimator(IWindowContainer wc, boolean scheduleFinishPip,
    PipTransitionAnimator getAnimator(SurfaceControl leash, boolean scheduleFinishPip,
            Rect startBounds, Rect endBounds) {
        if (mCurrentAnimator == null) {
            mCurrentAnimator = setupPipTransitionAnimator(
                    PipTransitionAnimator.ofBounds(wc, scheduleFinishPip, startBounds, endBounds));
                    PipTransitionAnimator.ofBounds(leash, scheduleFinishPip,
                            startBounds, endBounds));
        } else if (mCurrentAnimator.getAnimationType() == ANIM_TYPE_BOUNDS
                && mCurrentAnimator.isRunning()) {
            mCurrentAnimator.setDestinationBounds(endBounds);
@@ -94,7 +90,8 @@ public class PipAnimationController {
        } else {
            mCurrentAnimator.cancel();
            mCurrentAnimator = setupPipTransitionAnimator(
                    PipTransitionAnimator.ofBounds(wc, scheduleFinishPip, startBounds, endBounds));
                    PipTransitionAnimator.ofBounds(leash, scheduleFinishPip,
                            startBounds, endBounds));
        }
        return mCurrentAnimator;
    }
@@ -116,18 +113,18 @@ public class PipAnimationController {
        /**
         * Called when PiP animation is started.
         */
        public void onPipAnimationStart(IWindowContainer wc, PipTransitionAnimator animator) {}
        public void onPipAnimationStart(PipTransitionAnimator animator) {}

        /**
         * Called when PiP animation is ended.
         */
        public void onPipAnimationEnd(IWindowContainer wc, SurfaceControl.Transaction tx,
        public void onPipAnimationEnd(SurfaceControl.Transaction tx,
                PipTransitionAnimator animator) {}

        /**
         * Called when PiP animation is cancelled.
         */
        public void onPipAnimationCancel(IWindowContainer wc, PipTransitionAnimator animator) {}
        public void onPipAnimationCancel(PipTransitionAnimator animator) {}
    }

    /**
@@ -137,7 +134,6 @@ public class PipAnimationController {
    public abstract static class PipTransitionAnimator<T> extends ValueAnimator implements
            ValueAnimator.AnimatorUpdateListener,
            ValueAnimator.AnimatorListener {
        private final IWindowContainer mWindowContainer;
        private final boolean mScheduleFinishPip;
        private final SurfaceControl mLeash;
        private final @AnimationType int mAnimationType;
@@ -149,13 +145,11 @@ public class PipAnimationController {
        private PipAnimationCallback mPipAnimationCallback;
        private SurfaceControlTransactionFactory mSurfaceControlTransactionFactory;

        private PipTransitionAnimator(IWindowContainer wc, boolean scheduleFinishPip,
        private PipTransitionAnimator(SurfaceControl leash, boolean scheduleFinishPip,
                @AnimationType int animationType, Rect destinationBounds,
                T startValue, T endValue) {
            mWindowContainer = wc;
            mScheduleFinishPip = scheduleFinishPip;
            try {
                mLeash = wc.getLeash();
            mLeash = leash;
            mAnimationType = animationType;
            mDestinationBounds.set(destinationBounds);
            mStartValue = startValue;
@@ -163,9 +157,6 @@ public class PipAnimationController {
            addListener(this);
            addUpdateListener(this);
            mSurfaceControlTransactionFactory = SurfaceControl.Transaction::new;
            } catch (RemoteException e) {
                throw new RuntimeException(e);
            }
        }

        @Override
@@ -173,7 +164,7 @@ public class PipAnimationController {
            mCurrentValue = mStartValue;
            applySurfaceControlTransaction(mLeash, newSurfaceControlTransaction(), FRACTION_START);
            if (mPipAnimationCallback != null) {
                mPipAnimationCallback.onPipAnimationStart(mWindowContainer, this);
                mPipAnimationCallback.onPipAnimationStart(this);
            }
        }

@@ -189,14 +180,14 @@ public class PipAnimationController {
            final SurfaceControl.Transaction tx = newSurfaceControlTransaction();
            applySurfaceControlTransaction(mLeash, tx, FRACTION_END);
            if (mPipAnimationCallback != null) {
                mPipAnimationCallback.onPipAnimationEnd(mWindowContainer, tx, this);
                mPipAnimationCallback.onPipAnimationEnd(tx, this);
            }
        }

        @Override
        public void onAnimationCancel(Animator animation) {
            if (mPipAnimationCallback != null) {
                mPipAnimationCallback.onPipAnimationCancel(mWindowContainer, this);
                mPipAnimationCallback.onPipAnimationCancel(this);
            }
        }

@@ -260,9 +251,9 @@ public class PipAnimationController {
        abstract void applySurfaceControlTransaction(SurfaceControl leash,
                SurfaceControl.Transaction tx, float fraction);

        static PipTransitionAnimator<Float> ofAlpha(IWindowContainer wc, boolean scheduleFinishPip,
        static PipTransitionAnimator<Float> ofAlpha(SurfaceControl leash, boolean scheduleFinishPip,
                Rect destinationBounds, float startValue, float endValue) {
            return new PipTransitionAnimator<Float>(wc, scheduleFinishPip, ANIM_TYPE_ALPHA,
            return new PipTransitionAnimator<Float>(leash, scheduleFinishPip, ANIM_TYPE_ALPHA,
                    destinationBounds, startValue, endValue) {
                @Override
                void applySurfaceControlTransaction(SurfaceControl leash,
@@ -281,10 +272,10 @@ public class PipAnimationController {
            };
        }

        static PipTransitionAnimator<Rect> ofBounds(IWindowContainer wc, boolean scheduleFinishPip,
        static PipTransitionAnimator<Rect> ofBounds(SurfaceControl leash, boolean scheduleFinishPip,
                Rect startValue, Rect endValue) {
            // construct new Rect instances in case they are recycled
            return new PipTransitionAnimator<Rect>(wc, scheduleFinishPip, ANIM_TYPE_BOUNDS,
            return new PipTransitionAnimator<Rect>(leash, scheduleFinishPip, ANIM_TYPE_BOUNDS,
                    endValue, new Rect(startValue), new Rect(endValue)) {
                private final Rect mTmpRect = new Rect();

+216 −97

File changed.

Preview size limit exceeded, changes collapsed.

+19 −109
Original line number Diff line number Diff line
@@ -28,16 +28,11 @@ import android.graphics.Point;
import android.graphics.PointF;
import android.graphics.Rect;
import android.os.Debug;
import android.os.Handler;
import android.os.Message;
import android.os.RemoteException;
import android.util.Log;
import android.view.Choreographer;

import androidx.dynamicanimation.animation.SpringForce;

import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
import com.android.internal.os.SomeArgs;
import com.android.systemui.pip.PipSnapAlgorithm;
import com.android.systemui.pip.PipTaskOrganizer;
import com.android.systemui.shared.system.WindowManagerWrapper;
@@ -47,11 +42,12 @@ import com.android.systemui.util.animation.FloatProperties;
import com.android.systemui.util.animation.PhysicsAnimator;

import java.io.PrintWriter;
import java.util.function.Consumer;

/**
 * A helper to animate and manipulate the PiP.
 */
public class PipMotionHelper implements Handler.Callback, PipAppOpsListener.Callback,
public class PipMotionHelper implements PipAppOpsListener.Callback,
        FloatingContentCoordinator.FloatingContent {

    private static final String TAG = "PipMotionHelper";
@@ -68,14 +64,9 @@ public class PipMotionHelper implements Handler.Callback, PipAppOpsListener.Call
    // The fraction of the stack height that the user has to drag offscreen to dismiss the PiP
    private static final float DISMISS_OFFSCREEN_FRACTION = 0.3f;

    private static final int MSG_RESIZE_IMMEDIATE = 1;
    private static final int MSG_RESIZE_ANIMATE = 2;
    private static final int MSG_OFFSET_ANIMATE = 3;

    private final Context mContext;
    private final IActivityTaskManager mActivityTaskManager;
    private final PipTaskOrganizer mPipTaskOrganizer;
    private final Handler mHandler;

    private PipMenuActivityController mMenuController;
    private PipSnapAlgorithm mSnapAlgorithm;
@@ -92,9 +83,6 @@ public class PipMotionHelper implements Handler.Callback, PipAppOpsListener.Call
    /** The region that all of PIP must stay within. */
    private Rect mFloatingAllowedArea = new Rect();

    private final SfVsyncFrameCallbackProvider mSfVsyncFrameProvider =
            new SfVsyncFrameCallbackProvider();

    /**
     * Bounds that are animated using the physics animator.
     */
@@ -112,16 +100,11 @@ public class PipMotionHelper implements Handler.Callback, PipAppOpsListener.Call
    private PhysicsAnimator<Rect> mAnimatedBoundsPhysicsAnimator = PhysicsAnimator.getInstance(
            mAnimatedBounds);

    /** Callback that re-sizes PIP to the animated bounds. */
    private final Choreographer.FrameCallback mResizePipVsyncCallback =
            l -> resizePipUnchecked(mAnimatedBounds);

    /**
     * Update listener that posts a vsync frame callback to resize PIP to {@link #mAnimatedBounds}.
     * Update listener that resizes the PIP to {@link #mAnimatedBounds}.
     */
    private final PhysicsAnimator.UpdateListener<Rect> mResizePipVsyncUpdateListener =
            (target, values) ->
                    mSfVsyncFrameProvider.postFrameCallback(mResizePipVsyncCallback);
    private final PhysicsAnimator.UpdateListener<Rect> mResizePipUpdateListener =
            (target, values) -> resizePipUnchecked(mAnimatedBounds);

    /** FlingConfig instances provided to PhysicsAnimator for fling gestures. */
    private PhysicsAnimator.FlingConfig mFlingConfigX;
@@ -137,12 +120,13 @@ public class PipMotionHelper implements Handler.Callback, PipAppOpsListener.Call
                new PhysicsAnimator.SpringConfig(
                        SpringForce.STIFFNESS_LOW, SpringForce.DAMPING_RATIO_LOW_BOUNCY);

    private final Consumer<Rect> mUpdateBoundsCallback = (toBounds) -> mBounds.set(toBounds);

    public PipMotionHelper(Context context, IActivityTaskManager activityTaskManager,
            PipTaskOrganizer pipTaskOrganizer, PipMenuActivityController menuController,
            PipSnapAlgorithm snapAlgorithm, FlingAnimationUtils flingAnimationUtils,
            FloatingContentCoordinator floatingContentCoordinator) {
        mContext = context;
        mHandler = new Handler(ForegroundThread.get().getLooper(), this);
        mActivityTaskManager = activityTaskManager;
        mPipTaskOrganizer = pipTaskOrganizer;
        mMenuController = menuController;
@@ -234,7 +218,7 @@ public class PipMotionHelper implements Handler.Callback, PipAppOpsListener.Call
        }
        cancelAnimations();
        mMenuController.hideMenuWithoutResize();
        mHandler.post(() -> {
        mPipTaskOrganizer.getUpdateHandler().post(() -> {
            try {
                mActivityTaskManager.dismissPip(!skipAnimation, EXPAND_STACK_TO_FULLSCREEN_DURATION);
            } catch (RemoteException e) {
@@ -253,7 +237,7 @@ public class PipMotionHelper implements Handler.Callback, PipAppOpsListener.Call
        }
        cancelAnimations();
        mMenuController.hideMenuWithoutResize();
        mHandler.post(() -> {
        mPipTaskOrganizer.getUpdateHandler().post(() -> {
            try {
                mActivityTaskManager.removeStacksInWindowingModes(
                        new int[]{ WINDOWING_MODE_PINNED });
@@ -406,17 +390,13 @@ public class PipMotionHelper implements Handler.Callback, PipAppOpsListener.Call
     * Animates the PiP to offset it from the IME or shelf.
     */
    void animateToOffset(Rect originalBounds, int offset) {
        cancelAnimations();
        adjustAndAnimatePipOffset(originalBounds, offset, SHIFT_DURATION);
        if (DEBUG) {
            Log.d(TAG, "animateToOffset: originalBounds=" + originalBounds + " offset=" + offset
                    + " callers=\n" + Debug.getCallers(5, "    "));
        }

    private void adjustAndAnimatePipOffset(Rect originalBounds, int offset, int duration) {
        SomeArgs args = SomeArgs.obtain();
        args.arg1 = originalBounds;
        // offset would be zero if triggered from screen rotation.
        args.argi1 = offset;
        args.argi2 = duration;
        mHandler.sendMessage(mHandler.obtainMessage(MSG_OFFSET_ANIMATE, args));
        cancelAnimations();
        mPipTaskOrganizer.scheduleOffsetPip(originalBounds, offset, SHIFT_DURATION,
                mUpdateBoundsCallback);
    }

    /**
@@ -437,8 +417,7 @@ public class PipMotionHelper implements Handler.Callback, PipAppOpsListener.Call

    /**
     * Starts the physics animator which will update the animated PIP bounds using physics
     * animations, as well as the TimeAnimator which will apply those bounds to PIP at intervals
     * synchronized with the SurfaceFlinger vsync frame provider.
     * animations, as well as the TimeAnimator which will apply those bounds to PIP.
     *
     * This will also add end actions to the bounds animator that cancel the TimeAnimator and update
     * the 'real' bounds to equal the final animated bounds.
@@ -448,7 +427,7 @@ public class PipMotionHelper implements Handler.Callback, PipAppOpsListener.Call

        mAnimatedBoundsPhysicsAnimator
                .withEndActions(() ->  mPipTaskOrganizer.onMotionMovementEnd(mAnimatedBounds))
                .addUpdateListener(mResizePipVsyncUpdateListener)
                .addUpdateListener(mResizePipUpdateListener)
                .start();
    }

@@ -471,9 +450,7 @@ public class PipMotionHelper implements Handler.Callback, PipAppOpsListener.Call
                    + " callers=\n" + Debug.getCallers(5, "    "));
        }
        if (!toBounds.equals(mBounds)) {
            SomeArgs args = SomeArgs.obtain();
            args.arg1 = toBounds;
            mHandler.sendMessage(mHandler.obtainMessage(MSG_RESIZE_IMMEDIATE, args));
            mPipTaskOrganizer.scheduleResizePip(toBounds, mUpdateBoundsCallback);
        }
    }

@@ -486,10 +463,7 @@ public class PipMotionHelper implements Handler.Callback, PipAppOpsListener.Call
                    + " duration=" + duration + " callers=\n" + Debug.getCallers(5, "    "));
        }
        if (!toBounds.equals(mBounds)) {
            SomeArgs args = SomeArgs.obtain();
            args.arg1 = toBounds;
            args.argi1 = duration;
            mHandler.sendMessage(mHandler.obtainMessage(MSG_RESIZE_ANIMATE, args));
            mPipTaskOrganizer.scheduleAnimateResizePip(toBounds, duration, mUpdateBoundsCallback);
            setAnimatingToBounds(toBounds);
        }
    }
@@ -538,70 +512,6 @@ public class PipMotionHelper implements Handler.Callback, PipAppOpsListener.Call
        return dismissArea.contains(endpoint.x, endpoint.y);
    }

    /**
     * Handles messages to be processed on the background thread.
     */
    public boolean handleMessage(Message msg) {
        switch (msg.what) {
            case MSG_RESIZE_IMMEDIATE: {
                SomeArgs args = (SomeArgs) msg.obj;
                Rect toBounds = (Rect) args.arg1;
                mPipTaskOrganizer.resizePip(toBounds);
                mBounds.set(toBounds);
                return true;
            }

            case MSG_RESIZE_ANIMATE: {
                SomeArgs args = (SomeArgs) msg.obj;
                Rect toBounds = (Rect) args.arg1;
                int duration = args.argi1;
                try {
                    StackInfo stackInfo = mActivityTaskManager.getStackInfo(
                            WINDOWING_MODE_PINNED, ACTIVITY_TYPE_UNDEFINED);
                    if (stackInfo == null) {
                        // In the case where we've already re-expanded or dismissed the PiP, then
                        // just skip the resize
                        return true;
                    }

                    mPipTaskOrganizer.animateResizePip(toBounds, duration);
                    mBounds.set(toBounds);
                } catch (RemoteException e) {
                    Log.e(TAG, "Could not animate resize pinned stack to bounds: " + toBounds, e);
                }
                return true;
            }

            case MSG_OFFSET_ANIMATE: {
                SomeArgs args = (SomeArgs) msg.obj;
                Rect originalBounds = (Rect) args.arg1;
                final int offset = args.argi1;
                final int duration = args.argi2;
                try {
                    StackInfo stackInfo = mActivityTaskManager.getStackInfo(
                            WINDOWING_MODE_PINNED, ACTIVITY_TYPE_UNDEFINED);
                    if (stackInfo == null) {
                        // In the case where we've already re-expanded or dismissed the PiP, then
                        // just skip the resize
                        return true;
                    }

                    mPipTaskOrganizer.offsetPinnedStack(originalBounds,
                            0 /* xOffset */, offset, duration);
                    Rect toBounds = new Rect(originalBounds);
                    toBounds.offset(0, offset);
                    mBounds.set(toBounds);
                } catch (RemoteException e) {
                    Log.e(TAG, "Could not animate offset pinned stack with offset: " + offset, e);
                }
                return true;
            }

            default:
                return false;
        }
    }

    public void dump(PrintWriter pw, String prefix) {
        final String innerPrefix = prefix + "  ";
        pw.println(prefix + TAG);
+15 −10
Original line number Diff line number Diff line
@@ -21,33 +21,38 @@ import android.os.HandlerThread;

/**
 * Similar to {@link com.android.internal.os.BackgroundThread}, this is a shared singleton
 * foreground thread for each process.
 * foreground thread for each process for updating PIP.
 */
public final class ForegroundThread extends HandlerThread {
    private static ForegroundThread sInstance;
public final class PipUpdateThread extends HandlerThread {
    private static PipUpdateThread sInstance;
    private static Handler sHandler;

    private ForegroundThread() {
        super("recents.fg");
    private PipUpdateThread() {
        super("pip");
    }

    private static void ensureThreadLocked() {
        if (sInstance == null) {
            sInstance = new ForegroundThread();
            sInstance = new PipUpdateThread();
            sInstance.start();
            sHandler = new Handler(sInstance.getLooper());
        }
    }

    public static ForegroundThread get() {
        synchronized (ForegroundThread.class) {
    /**
     * @return the static update thread instance
     */
    public static PipUpdateThread get() {
        synchronized (PipUpdateThread.class) {
            ensureThreadLocked();
            return sInstance;
        }
    }

    /**
     * @return the static update thread handler instance
     */
    public static Handler getHandler() {
        synchronized (ForegroundThread.class) {
        synchronized (PipUpdateThread.class) {
            ensureThreadLocked();
            return sHandler;
        }
Loading