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

This commit is contained in:
Winson Chung
2021-06-25 22:11:04 +00:00
committed by Android (Google) Code Review
6 changed files with 124 additions and 201 deletions

View File

@@ -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

View File

@@ -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,

View File

@@ -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;
}
}
}
cancelAnimation(REORDER_MOVE_TO_ORIGINAL_POSITION, "binderDied");
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 onCleanup() {
if (mFinishTransaction != null) {
final Transaction pendingTransaction = mTask.getPendingTransaction();
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() {
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;

View File

@@ -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);

View File

@@ -1,120 +0,0 @@
/*
* 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;
}
}

View File

@@ -255,7 +255,6 @@ public class RecentsAnimationControllerTest extends WindowTestsBase {
mController.setDeferredCancel(true /* deferred */, false /* screenshot */);
mController.cancelAnimationWithScreenshot(false /* screenshot */);
verify(mMockRunner).onAnimationCanceled(null /* taskSnapshot */);
assertNull(mController.mRecentScreenshotAnimator);
// Simulate the app transition finishing
mController.mAppTransitionListener.onAppTransitionStartingLocked(false, 0, 0, 0);
@@ -271,7 +270,8 @@ public class RecentsAnimationControllerTest extends WindowTestsBase {
assertEquals(activity.getTask().getTopVisibleActivity(), activity);
assertEquals(activity.findMainWindow(), win1);
mController.addAnimation(activity.getTask(), false /* isRecentTaskInvisible */);
RecentsAnimationController.TaskAnimationAdapter adapter = mController.addAnimation(
activity.getTask(), false /* isRecentTaskInvisible */);
assertTrue(mController.isAnimatingTask(activity.getTask()));
spyOn(mWm.mTaskSnapshotController);
@@ -282,14 +282,9 @@ public class RecentsAnimationControllerTest extends WindowTestsBase {
mController.setDeferredCancel(true /* deferred */, true /* screenshot */);
mController.cancelAnimationWithScreenshot(true /* screenshot */);
verify(mMockRunner).onAnimationCanceled(mMockTaskSnapshot /* taskSnapshot */);
assertNotNull(mController.mRecentScreenshotAnimator);
assertTrue(mController.mRecentScreenshotAnimator.isAnimating());
// Assume IRecentsAnimationController#cleanupScreenshot called to finish screenshot
// animation.
spyOn(mController.mRecentScreenshotAnimator.mAnimatable);
mController.mRecentScreenshotAnimator.cancelAnimation();
verify(mController.mRecentScreenshotAnimator.mAnimatable).onAnimationLeashLost(any());
// Continue the animation (simulating a call to cleanupScreenshot())
mController.continueDeferredCancelAnimation();
verify(mAnimationCallbacks).onAnimationFinished(REORDER_KEEP_IN_PLACE, false);
}
@@ -685,6 +680,29 @@ public class RecentsAnimationControllerTest extends WindowTestsBase {
verify(mAnimationCallbacks).onAnimationFinished(REORDER_MOVE_TO_ORIGINAL_POSITION, false);
}
@Test
public void testCancelForStartHome() throws Exception {
mWm.setRecentsAnimationController(mController);
final ActivityRecord activity = createActivityRecord(mDefaultDisplay);
final WindowState win1 = createWindow(null, TYPE_BASE_APPLICATION, activity, "win1");
activity.addWindow(win1);
RecentsAnimationController.TaskAnimationAdapter adapter = mController.addAnimation(
activity.getTask(), false /* isRecentTaskInvisible */);
mController.setWillFinishToHome(true);
// Verify cancel is called with a snapshot and that we've created an overlay
spyOn(mWm.mTaskSnapshotController);
doReturn(mMockTaskSnapshot).when(mWm.mTaskSnapshotController).getSnapshot(anyInt(),
anyInt(), eq(false) /* restoreFromDisk */, eq(false) /* isLowResolution */);
mController.cancelAnimationForHomeStart();
verify(mMockRunner).onAnimationCanceled(any());
// Continue the animation (simulating a call to cleanupScreenshot())
mController.continueDeferredCancelAnimation();
verify(mAnimationCallbacks).onAnimationFinished(REORDER_MOVE_TO_TOP, false);
}
private ActivityRecord createHomeActivity() {
final ActivityRecord homeActivity = new ActivityBuilder(mWm.mAtmService)
.setParentTask(mRootHomeTask)