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

Commit ab76bbc2 authored by Winson Chung's avatar Winson Chung
Browse files

Ensure that PiP mode changed callback if animation is interrupted

- If a PiP enter animation callback is interrupted, the activity is never
  actually put into PiP mode, and will never receive
  onPictureInPictureModeChanged(false) even if enterPictureInPictureMode()
  returns true due to the change being deduped (it was never in that mode).
  In this specific case, force a callback to be made to the app so that it
  has a signal that it is no longer in PiP mode.

Bug: 63749396
Test: bit FrameworksServicesTests:com.android.server.wm.BoundsAnimationControllerTests
Test: android.server.cts.ActivityManagerPinnedStackTests
Test: #testEnterPipInterruptedCallbacks

Change-Id: I301c70e4fb0f2175dd6d7b5feae065b41df2878d
parent 02584a23
Loading
Loading
Loading
Loading
+3 −4
Original line number Diff line number Diff line
@@ -659,14 +659,14 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo
        }
    }

    void updatePictureInPictureMode(Rect targetStackBounds) {
    void updatePictureInPictureMode(Rect targetStackBounds, boolean forceUpdate) {
        if (task == null || task.getStack() == null || app == null || app.thread == null) {
            return;
        }

        final boolean inPictureInPictureMode = (task.getStackId() == PINNED_STACK_ID) &&
                (targetStackBounds != null);
        if (inPictureInPictureMode != mLastReportedPictureInPictureMode) {
        if (inPictureInPictureMode != mLastReportedPictureInPictureMode || forceUpdate) {
            // Picture-in-picture mode changes also trigger a multi-window mode change as well, so
            // update that here in order
            mLastReportedPictureInPictureMode = inPictureInPictureMode;
@@ -681,8 +681,7 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo
    private void schedulePictureInPictureModeChanged(Configuration overrideConfig) {
        try {
            app.thread.schedulePictureInPictureModeChanged(appToken,
                    mLastReportedPictureInPictureMode,
                    overrideConfig);
                    mLastReportedPictureInPictureMode, overrideConfig);
        } catch (Exception e) {
            // If process died, no one cares.
        }
+21 −22
Original line number Diff line number Diff line
@@ -4411,21 +4411,10 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
            return;
        }

        scheduleUpdatePictureInPictureModeIfNeeded(task, stack.mBounds, false /* immediate */);
        scheduleUpdatePictureInPictureModeIfNeeded(task, stack.mBounds);
    }

    void scheduleUpdatePictureInPictureModeIfNeeded(TaskRecord task, Rect targetStackBounds,
            boolean immediate) {

        if (immediate) {
            mHandler.removeMessages(REPORT_PIP_MODE_CHANGED_MSG);
            for (int i = task.mActivities.size() - 1; i >= 0; i--) {
                final ActivityRecord r = task.mActivities.get(i);
                if (r.app != null && r.app.thread != null) {
                    r.updatePictureInPictureMode(targetStackBounds);
                }
            }
        } else {
    void scheduleUpdatePictureInPictureModeIfNeeded(TaskRecord task, Rect targetStackBounds) {
        for (int i = task.mActivities.size() - 1; i >= 0; i--) {
            final ActivityRecord r = task.mActivities.get(i);
            if (r.app != null && r.app.thread != null) {
@@ -4438,6 +4427,15 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
            mHandler.sendEmptyMessage(REPORT_PIP_MODE_CHANGED_MSG);
        }
    }

    void updatePictureInPictureMode(TaskRecord task, Rect targetStackBounds, boolean forceUpdate) {
        mHandler.removeMessages(REPORT_PIP_MODE_CHANGED_MSG);
        for (int i = task.mActivities.size() - 1; i >= 0; i--) {
            final ActivityRecord r = task.mActivities.get(i);
            if (r.app != null && r.app.thread != null) {
                r.updatePictureInPictureMode(targetStackBounds, forceUpdate);
            }
        }
    }

    void setDockedStackMinimized(boolean minimized) {
@@ -4497,7 +4495,8 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
                    synchronized (mService) {
                        for (int i = mPipModeChangedActivities.size() - 1; i >= 0; i--) {
                            final ActivityRecord r = mPipModeChangedActivities.remove(i);
                            r.updatePictureInPictureMode(mPipModeChangedTargetStackBounds);
                            r.updatePictureInPictureMode(mPipModeChangedTargetStackBounds,
                                    false /* forceUpdate */);
                        }
                    }
                } break;
+4 −3
Original line number Diff line number Diff line
@@ -91,15 +91,16 @@ class PinnedActivityStack extends ActivityStack<PinnedStackWindowController>
        return mWindowContainerController.deferScheduleMultiWindowModeChanged();
    }

    public void updatePictureInPictureModeForPinnedStackAnimation(Rect targetStackBounds) {
    public void updatePictureInPictureModeForPinnedStackAnimation(Rect targetStackBounds,
            boolean forceUpdate) {
        // It is guaranteed that the activities requiring the update will be in the pinned stack at
        // this point (either reparented before the animation into PiP, or before reparenting after
        // the animation out of PiP)
        synchronized(this) {
            ArrayList<TaskRecord> tasks = getAllTasks();
            for (int i = 0; i < tasks.size(); i++ ) {
                mStackSupervisor.scheduleUpdatePictureInPictureModeIfNeeded(tasks.get(i),
                        targetStackBounds, true /* immediate */);
                mStackSupervisor.updatePictureInPictureMode(tasks.get(i), targetStackBounds,
                        forceUpdate);
            }
        }
    }
+25 −16
Original line number Diff line number Diff line
@@ -21,7 +21,6 @@ import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;

import android.animation.AnimationHandler;
import android.animation.AnimationHandler.AnimationFrameCallbackProvider;
import android.animation.Animator;
import android.animation.ValueAnimator;
import android.annotation.IntDef;
@@ -32,13 +31,11 @@ import android.os.IBinder;
import android.os.Debug;
import android.util.ArrayMap;
import android.util.Slog;
import android.view.Choreographer;
import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
import android.view.WindowManagerInternal;

import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.graphics.SfVsyncFrameCallbackProvider;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -142,9 +139,6 @@ public class BoundsAnimationController {
        // True if this this animation was canceled and will be replaced the another animation from
        // the same {@link #BoundsAnimationTarget} target.
        private boolean mSkipFinalResize;
        // True if this animation replaced a previous animation of the same
        // {@link #BoundsAnimationTarget} target.
        private final boolean mSkipAnimationStart;
        // True if this animation was canceled by the user, not as a part of a replacing animation
        private boolean mSkipAnimationEnd;

@@ -159,6 +153,7 @@ public class BoundsAnimationController {

        // Whether to schedule PiP mode changes on animation start/end
        private @SchedulePipModeChangedState int mSchedulePipModeChangedState;
        private @SchedulePipModeChangedState int mPrevSchedulePipModeChangedState;

        // Depending on whether we are animating from
        // a smaller to a larger size
@@ -171,14 +166,14 @@ public class BoundsAnimationController {

        BoundsAnimator(BoundsAnimationTarget target, Rect from, Rect to,
                @SchedulePipModeChangedState int schedulePipModeChangedState,
                boolean moveFromFullscreen, boolean moveToFullscreen,
                boolean replacingExistingAnimation) {
                @SchedulePipModeChangedState int prevShedulePipModeChangedState,
                boolean moveFromFullscreen, boolean moveToFullscreen) {
            super();
            mTarget = target;
            mFrom.set(from);
            mTo.set(to);
            mSkipAnimationStart = replacingExistingAnimation;
            mSchedulePipModeChangedState = schedulePipModeChangedState;
            mPrevSchedulePipModeChangedState = prevShedulePipModeChangedState;
            mMoveFromFullscreen = moveFromFullscreen;
            mMoveToFullscreen = moveToFullscreen;
            addUpdateListener(this);
@@ -200,7 +195,7 @@ public class BoundsAnimationController {
        @Override
        public void onAnimationStart(Animator animation) {
            if (DEBUG) Slog.d(TAG, "onAnimationStart: mTarget=" + mTarget
                    + " mSkipAnimationStart=" + mSkipAnimationStart
                    + " mPrevSchedulePipModeChangedState=" + mPrevSchedulePipModeChangedState
                    + " mSchedulePipModeChangedState=" + mSchedulePipModeChangedState);
            mFinishAnimationAfterTransition = false;
            mTmpRect.set(mFrom.left, mFrom.top, mFrom.left + mFrozenTaskWidth,
@@ -210,18 +205,26 @@ public class BoundsAnimationController {
            // running
            updateBooster();

            // Ensure that we have prepared the target for animation before
            // we trigger any size changes, so it can swap surfaces
            // in to appropriate modes, or do as it wishes otherwise.
            if (!mSkipAnimationStart) {
            // Ensure that we have prepared the target for animation before we trigger any size
            // changes, so it can swap surfaces in to appropriate modes, or do as it wishes
            // otherwise.
            if (mPrevSchedulePipModeChangedState == NO_PIP_MODE_CHANGED_CALLBACKS) {
                mTarget.onAnimationStart(mSchedulePipModeChangedState ==
                        SCHEDULE_PIP_MODE_CHANGED_ON_START);
                        SCHEDULE_PIP_MODE_CHANGED_ON_START, false /* forceUpdate */);

                // When starting an animation from fullscreen, pause here and wait for the
                // windows-drawn signal before we start the rest of the transition down into PiP.
                if (mMoveFromFullscreen) {
                    pause();
                }
            } else if (mPrevSchedulePipModeChangedState == SCHEDULE_PIP_MODE_CHANGED_ON_END &&
                    mSchedulePipModeChangedState == SCHEDULE_PIP_MODE_CHANGED_ON_START) {
                // We are replacing a running animation into PiP, but since it hasn't completed, the
                // client will not currently receive any picture-in-picture mode change callbacks.
                // However, we still need to report to them that they are leaving PiP, so this will
                // force an update via a mode changed callback.
                mTarget.onAnimationStart(true /* schedulePipModeChangedCallback */,
                        true /* forceUpdate */);
            }

            // Immediately update the task bounds if they have to become larger, but preserve
@@ -388,6 +391,8 @@ public class BoundsAnimationController {
            boolean moveFromFullscreen, boolean moveToFullscreen) {
        final BoundsAnimator existing = mRunningAnimations.get(target);
        final boolean replacing = existing != null;
        @SchedulePipModeChangedState int prevSchedulePipModeChangedState =
                NO_PIP_MODE_CHANGED_CALLBACKS;

        if (DEBUG) Slog.d(TAG, "animateBounds: target=" + target + " from=" + from + " to=" + to
                + " schedulePipModeChangedState=" + schedulePipModeChangedState
@@ -403,6 +408,9 @@ public class BoundsAnimationController {
                return existing;
            }

            // Save the previous state
            prevSchedulePipModeChangedState = existing.mSchedulePipModeChangedState;

            // Update the PiP callback states if we are replacing the animation
            if (existing.mSchedulePipModeChangedState == SCHEDULE_PIP_MODE_CHANGED_ON_START) {
                if (schedulePipModeChangedState == SCHEDULE_PIP_MODE_CHANGED_ON_START) {
@@ -428,7 +436,8 @@ public class BoundsAnimationController {
            existing.cancel();
        }
        final BoundsAnimator animator = new BoundsAnimator(target, from, to,
                schedulePipModeChangedState, moveFromFullscreen, moveToFullscreen, replacing);
                schedulePipModeChangedState, prevSchedulePipModeChangedState,
                moveFromFullscreen, moveToFullscreen);
        mRunningAnimations.put(target, animator);
        animator.setFloatValues(0f, 1f);
        animator.setDuration((animationDuration != -1 ? animationDuration
+1 −1
Original line number Diff line number Diff line
@@ -31,7 +31,7 @@ interface BoundsAnimationTarget {
     * @param schedulePipModeChangedCallback whether or not to schedule the PiP mode changed
     * callbacks
     */
    void onAnimationStart(boolean schedulePipModeChangedCallback);
    void onAnimationStart(boolean schedulePipModeChangedCallback, boolean forceUpdate);

    /**
     * Sets the size of the target (without any intermediate steps, like scheduling animation)
Loading