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

Commit 1da53a25 authored by Winson Chung's avatar Winson Chung Committed by Android (Google) Code Review
Browse files

Merge "Fix flash when canceling the recents animation with a screenshot" into sc-dev

parents 201eec73 a2eaf83a
Loading
Loading
Loading
Loading
+0 −5
Original line number Diff line number Diff line
@@ -279,11 +279,6 @@ public abstract class ActivityTaskManagerInternal {
     */
    public abstract boolean isRecentsComponentHomeActivity(int userId);

    /**
     * Cancels any currently running recents animation.
     */
    public abstract void cancelRecentsAnimation(boolean restoreHomeRootTaskPosition);

    /**
     * Returns true if the app can close system dialogs. Otherwise it either throws a {@link
     * SecurityException} or returns false with a logcat message depending on whether the app
+0 −5
Original line number Diff line number Diff line
@@ -5255,11 +5255,6 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
            return getRecentTasks().isRecentsComponentHomeActivity(userId);
        }

        @Override
        public void cancelRecentsAnimation(boolean restoreHomeRootTaskPosition) {
            ActivityTaskManagerService.this.cancelRecentsAnimation(restoreHomeRootTaskPosition);
        }

        @Override
        public boolean checkCanCloseSystemDialogs(int pid, int uid, @Nullable String packageName) {
            return ActivityTaskManagerService.this.checkCanCloseSystemDialogs(pid, uid,
+93 −60
Original line number Diff line number Diff line
@@ -36,8 +36,10 @@ import static com.android.server.wm.WindowManagerInternal.AppTransitionListener;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.app.WindowConfiguration;
import android.graphics.GraphicBuffer;
import android.graphics.Point;
import android.graphics.Rect;
import android.hardware.HardwareBuffer;
import android.os.Binder;
import android.os.IBinder.DeathRecipient;
import android.os.RemoteException;
@@ -54,6 +56,7 @@ import android.view.InputWindowHandle;
import android.view.RemoteAnimationTarget;
import android.view.SurfaceControl;
import android.view.SurfaceControl.Transaction;
import android.view.SurfaceSession;
import android.view.WindowInsets.Type;
import android.window.PictureInPictureSurfaceTransaction;
import android.window.TaskSnapshot;
@@ -150,6 +153,8 @@ public class RecentsAnimationController implements DeathRecipient {
    private boolean mCancelOnNextTransitionStart;
    // Whether to take a screenshot when handling a deferred cancel
    private boolean mCancelDeferredWithScreenshot;
    // The reorder mode to apply after the cleanupScreenshot() callback
    private int mPendingCancelWithScreenshotReorderMode = REORDER_MOVE_TO_ORIGINAL_POSITION;

    @VisibleForTesting
    boolean mIsAddingTaskToTargets;
@@ -158,12 +163,6 @@ public class RecentsAnimationController implements DeathRecipient {
    private boolean mNavigationBarAttachedToApp;
    private ActivityRecord mNavBarAttachedApp;

    /**
     * Animates the screenshot of task that used to be controlled by RecentsAnimation.
     * @see {@link #setCancelOnNextTransitionStart}
     */
    SurfaceAnimator mRecentScreenshotAnimator;

    /**
     * An app transition listener to cancel the recents animation only after the app transition
     * starts or is canceled.
@@ -355,12 +354,9 @@ public class RecentsAnimationController implements DeathRecipient {
        public void cleanupScreenshot() {
            final long token = Binder.clearCallingIdentity();
            try {
                synchronized (mService.mGlobalLock) {
                    if (mRecentScreenshotAnimator != null) {
                        mRecentScreenshotAnimator.cancelAnimation();
                        mRecentScreenshotAnimator = null;
                    }
                }
                // Note, the callback will handle its own synchronization, do not lock on WM lock
                // prior to calling the callback
                continueDeferredCancelAnimation();
            } finally {
                Binder.restoreCallingIdentity(token);
            }
@@ -519,13 +515,13 @@ public class RecentsAnimationController implements DeathRecipient {
    }

    @VisibleForTesting
    AnimationAdapter addAnimation(Task task, boolean isRecentTaskInvisible) {
    TaskAnimationAdapter addAnimation(Task task, boolean isRecentTaskInvisible) {
        return addAnimation(task, isRecentTaskInvisible, false /* hidden */,
                null /* finishedCallback */);
    }

    @VisibleForTesting
    AnimationAdapter addAnimation(Task task, boolean isRecentTaskInvisible, boolean hidden,
    TaskAnimationAdapter addAnimation(Task task, boolean isRecentTaskInvisible, boolean hidden,
            OnAnimationFinishedCallback finishedCallback) {
        ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS, "addAnimation(%s)", task.getName());
        final TaskAnimationAdapter taskAdapter = new TaskAnimationAdapter(task,
@@ -541,9 +537,7 @@ public class RecentsAnimationController implements DeathRecipient {
    void removeAnimation(TaskAnimationAdapter taskAdapter) {
        ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS,
                "removeAnimation(%d)", taskAdapter.mTask.mTaskId);
        taskAdapter.mTask.setCanAffectSystemUiFlags(true);
        taskAdapter.mCapturedFinishCallback.onAnimationFinished(taskAdapter.mLastAnimationType,
                taskAdapter);
        taskAdapter.onRemove();
        mPendingAnimations.remove(taskAdapter);
    }

@@ -821,8 +815,22 @@ public class RecentsAnimationController implements DeathRecipient {
    }

    /**
     * If there is a recents animation running, we need to cancel the animation and snapshot the
     * tasks before the change (to ensure they are captured at the right configuration)
     * Cancels the running animation when starting home, providing a snapshot for the runner to
     * properly handle the cancellation. This call uses the provided hint to determine how to
     * finish the animation.
     */
    public void cancelAnimationForHomeStart() {
        if (mCanceled) {
            return;
        }
        cancelAnimation(mWillFinishToHome ? REORDER_MOVE_TO_TOP : REORDER_KEEP_IN_PLACE,
                true /* screenshot */, "cancelAnimationForHomeStart");
    }

    /**
     * Cancels the running animation when there is a display change, providing a snapshot for the
     * runner to properly handle the cancellation. This call uses the provided hint to determine
     * how to finish the animation.
     */
    public void cancelAnimationForDisplayChange() {
        if (mCanceled) {
@@ -843,16 +851,22 @@ public class RecentsAnimationController implements DeathRecipient {
            mCanceled = true;

            if (screenshot && !mPendingAnimations.isEmpty()) {
                final TaskAnimationAdapter adapter = mPendingAnimations.get(0);
                final Task task = adapter.mTask;
                // Screen shot previous task when next task starts transition and notify the runner.
                // We will actually finish the animation once the runner calls cleanUpScreenshot().
                final Task task = mPendingAnimations.get(0).mTask;
                final TaskSnapshot taskSnapshot = screenshotRecentTask(task, reorderMode);
                final TaskSnapshot taskSnapshot = screenshotRecentTask(task);
                mPendingCancelWithScreenshotReorderMode = reorderMode;
                try {
                    mRunner.onAnimationCanceled(taskSnapshot);
                } catch (RemoteException e) {
                    Slog.e(TAG, "Failed to cancel recents animation", e);
                }
                if (taskSnapshot == null) {
                if (taskSnapshot != null) {
                    // Defer until the runner calls back to cleanupScreenshot()
                    adapter.setSnapshotOverlay(taskSnapshot);
                } else {
                    // Do a normal cancel since we couldn't screenshot
                    mCallbacks.onAnimationFinished(reorderMode, false /* sendUserLeaveHint */);
                }
            } else {
@@ -869,6 +883,12 @@ public class RecentsAnimationController implements DeathRecipient {
        }
    }

    @VisibleForTesting
    void continueDeferredCancelAnimation() {
        mCallbacks.onAnimationFinished(mPendingCancelWithScreenshotReorderMode,
                false /* sendUserLeaveHint */);
    }

    @VisibleForTesting
    void setWillFinishToHome(boolean willFinishToHome) {
        mWillFinishToHome = willFinishToHome;
@@ -915,29 +935,13 @@ public class RecentsAnimationController implements DeathRecipient {
        return mRequestDeferCancelUntilNextTransition && mCancelDeferredWithScreenshot;
    }

    TaskSnapshot screenshotRecentTask(Task task, @ReorderMode int reorderMode) {
    TaskSnapshot screenshotRecentTask(Task task) {
        final TaskSnapshotController snapshotController = mService.mTaskSnapshotController;
        final ArraySet<Task> tasks = Sets.newArraySet(task);
        snapshotController.snapshotTasks(tasks);
        snapshotController.addSkipClosingAppSnapshotTasks(tasks);
        final TaskSnapshot taskSnapshot = snapshotController.getSnapshot(task.mTaskId,
                task.mUserId, false /* restoreFromDisk */, false /* isLowResolution */);
        if (taskSnapshot == null) {
            return null;
        }

        final TaskScreenshotAnimatable animatable = new TaskScreenshotAnimatable(
                mService.mSurfaceControlFactory, task,
                new SurfaceControl.ScreenshotHardwareBuffer(taskSnapshot.getHardwareBuffer(),
                        taskSnapshot.getColorSpace(), false /* containsSecureLayers */));
        mRecentScreenshotAnimator = new SurfaceAnimator(
                animatable,
                (type, anim) -> {
                    ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS, "mRecentScreenshotAnimator finish");
                    mCallbacks.onAnimationFinished(reorderMode, false /* sendUserLeaveHint */);
                }, mService);
        mRecentScreenshotAnimator.transferAnimation(task.mSurfaceAnimator);
        return taskSnapshot;
        return snapshotController.getSnapshot(task.mTaskId, task.mUserId,
                false /* restoreFromDisk */, false /* isLowResolution */);
    }

    void cleanupAnimation(@ReorderMode int reorderMode) {
@@ -976,12 +980,6 @@ public class RecentsAnimationController implements DeathRecipient {
        mRunner = null;
        mCanceled = true;

        // Make sure previous animator has cleaned-up.
        if (mRecentScreenshotAnimator != null) {
            mRecentScreenshotAnimator.cancelAnimation();
            mRecentScreenshotAnimator = null;
        }

        // Restore IME icon only when moving the original app task to front from recents, in case
        // IME icon may missing if the moving task has already been the current focused task.
        if (reorderMode == REORDER_MOVE_TO_ORIGINAL_POSITION && !mIsAddingTaskToTargets) {
@@ -1028,16 +1026,7 @@ public class RecentsAnimationController implements DeathRecipient {

    @Override
    public void binderDied() {
        if (!mCanceled) {
        cancelAnimation(REORDER_MOVE_TO_ORIGINAL_POSITION, "binderDied");
        } else {
            synchronized (mService.getWindowManagerLock()) {
                if (mRecentScreenshotAnimator != null) {
                    mRecentScreenshotAnimator.cancelAnimation();
                    mRecentScreenshotAnimator = null;
                }
            }
        }

        synchronized (mService.getWindowManagerLock()) {
            // Clear associated input consumers on runner death
@@ -1171,6 +1160,8 @@ public class RecentsAnimationController implements DeathRecipient {
        private PictureInPictureSurfaceTransaction mFinishTransaction;
        // An overlay used to mask the content as an app goes into PIP
        private SurfaceControl mFinishOverlay;
        // An overlay used for canceling the animation with a screenshot
        private SurfaceControl mSnapshotOverlay;

        TaskAnimationAdapter(Task task, boolean isRecentTaskInvisible) {
            mTask = task;
@@ -1205,10 +1196,47 @@ public class RecentsAnimationController implements DeathRecipient {
            return mTarget;
        }

        void setSnapshotOverlay(TaskSnapshot snapshot) {
            // Create a surface control for the snapshot and reparent it to the leash
            final HardwareBuffer buffer = snapshot.getHardwareBuffer();
            if (buffer == null) {
                return;
            }

            final SurfaceSession session = new SurfaceSession();
            mSnapshotOverlay = mService.mSurfaceControlFactory.apply(session)
                    .setName("RecentTaskScreenshotSurface")
                    .setCallsite("TaskAnimationAdapter.setSnapshotOverlay")
                    .setFormat(buffer.getFormat())
                    .setParent(mCapturedLeash)
                    .setBLASTLayer()
                    .build();

            final float scale = 1.0f * mTask.getBounds().width() / buffer.getWidth();
            mTask.getPendingTransaction()
                    .setBuffer(mSnapshotOverlay, GraphicBuffer.createFromHardwareBuffer(buffer))
                    .setColorSpace(mSnapshotOverlay, snapshot.getColorSpace())
                    .setLayer(mSnapshotOverlay, Integer.MAX_VALUE)
                    .setMatrix(mSnapshotOverlay, scale, 0, 0, scale)
                    .show(mSnapshotOverlay)
                    .apply();
        }

        void onRemove() {
            if (mSnapshotOverlay != null) {
                // Clean up the snapshot overlay if necessary
                mTask.getPendingTransaction()
                        .remove(mSnapshotOverlay)
                        .apply();
                mSnapshotOverlay = null;
            }
            mTask.setCanAffectSystemUiFlags(true);
            mCapturedFinishCallback.onAnimationFinished(mLastAnimationType, this);
        }

        void onCleanup() {
            if (mFinishTransaction != null) {
            final Transaction pendingTransaction = mTask.getPendingTransaction();

            if (mFinishTransaction != null) {
                // Reparent the overlay
                if (mFinishOverlay != null) {
                    pendingTransaction.reparent(mFinishOverlay, mTask.mSurfaceControl);
@@ -1236,10 +1264,15 @@ public class RecentsAnimationController implements DeathRecipient {
            } else if (!mTask.isAttached()) {
                // Apply the task's pending transaction in case it is detached and its transaction
                // is not reachable.
                mTask.getPendingTransaction().apply();
                pendingTransaction.apply();
            }
        }

        @VisibleForTesting
        public SurfaceControl getSnapshotOverlay() {
            return mSnapshotOverlay;
        }

        @Override
        public boolean getShowWallpaper() {
            return false;
+3 −1
Original line number Diff line number Diff line
@@ -1529,7 +1529,9 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
        // Updates the extra information of the intent.
        if (fromHomeKey) {
            homeIntent.putExtra(WindowManagerPolicy.EXTRA_FROM_HOME_KEY, true);
            mWindowManager.cancelRecentsAnimation(REORDER_KEEP_IN_PLACE, "startHomeActivity");
            if (mWindowManager.getRecentsAnimationController() != null) {
                mWindowManager.getRecentsAnimationController().cancelAnimationForHomeStart();
            }
        }
        homeIntent.putExtra(WindowManagerPolicy.EXTRA_START_REASON, reason);

+0 −120
Original line number Diff line number Diff line
/*
 * Copyright (C) 2019 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.android.server.wm;

import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_RECENTS_ANIMATIONS;

import android.graphics.GraphicBuffer;
import android.hardware.HardwareBuffer;
import android.view.SurfaceControl;
import android.view.SurfaceSession;

import com.android.internal.protolog.common.ProtoLog;

import java.util.function.Function;

/**
 * Class used by {@link RecentsAnimationController} to create a surface control with taking
 * screenshot of task when canceling recents animation.
 *
 * @see {@link RecentsAnimationController#setCancelOnNextTransitionStart}
 */
class TaskScreenshotAnimatable implements SurfaceAnimator.Animatable {
    private static final String TAG = "TaskScreenshotAnim";
    private Task mTask;
    private SurfaceControl mSurfaceControl;
    private int mWidth;
    private int mHeight;

    TaskScreenshotAnimatable(Function<SurfaceSession, SurfaceControl.Builder> surfaceControlFactory,
            Task task, SurfaceControl.ScreenshotHardwareBuffer screenshotBuffer) {
        HardwareBuffer buffer = screenshotBuffer == null
                ? null : screenshotBuffer.getHardwareBuffer();
        mTask = task;
        mWidth = (buffer != null) ? buffer.getWidth() : 1;
        mHeight = (buffer != null) ? buffer.getHeight() : 1;
        ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS,
                "Creating TaskScreenshotAnimatable: task: %s width: %d height: %d",
                        task, mWidth, mHeight);
        mSurfaceControl = surfaceControlFactory.apply(new SurfaceSession())
                .setName("RecentTaskScreenshotSurface")
                .setBLASTLayer()
                .setCallsite("TaskScreenshotAnimatable")
                .build();
        if (buffer != null) {
            GraphicBuffer graphicBuffer = GraphicBuffer.createFromHardwareBuffer(buffer);
            getPendingTransaction().setBuffer(mSurfaceControl, graphicBuffer);
            getPendingTransaction().setColorSpace(mSurfaceControl,
                    screenshotBuffer.getColorSpace());
            final float scale = 1.0f * mTask.getBounds().width() / mWidth;
            getPendingTransaction().setMatrix(mSurfaceControl, scale, 0, 0, scale);
        }
        getPendingTransaction().show(mSurfaceControl);
    }

    @Override
    public SurfaceControl.Transaction getPendingTransaction() {
        return mTask.getPendingTransaction();
    }

    @Override
    public void commitPendingTransaction() {
        mTask.commitPendingTransaction();
    }

    @Override
    public void onAnimationLeashCreated(SurfaceControl.Transaction t, SurfaceControl leash) {
        t.setLayer(leash, 1);
    }

    @Override
    public void onAnimationLeashLost(SurfaceControl.Transaction t) {
        if (mSurfaceControl != null) {
            t.remove(mSurfaceControl);
            mSurfaceControl = null;
        }
    }

    @Override
    public SurfaceControl.Builder makeAnimationLeash() {
        return mTask.makeAnimationLeash();
    }

    @Override
    public SurfaceControl getAnimationLeashParent() {
        return mTask.getAnimationLeashParent();
    }

    @Override
    public SurfaceControl getSurfaceControl() {
        return mSurfaceControl;
    }

    @Override
    public SurfaceControl getParentSurfaceControl() {
        return mTask.mSurfaceControl;
    }

    @Override
    public int getSurfaceWidth() {
        return mWidth;
    }

    @Override
    public int getSurfaceHeight() {
        return mHeight;
    }
}
Loading