summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--services/core/java/com/android/server/wm/SurfaceAnimator.java47
-rw-r--r--services/core/java/com/android/server/wm/SurfaceFreezer.java43
-rw-r--r--services/core/java/com/android/server/wm/WindowContainer.java30
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java67
4 files changed, 146 insertions, 41 deletions
diff --git a/services/core/java/com/android/server/wm/SurfaceAnimator.java b/services/core/java/com/android/server/wm/SurfaceAnimator.java
index c7bf8ecfe949..d712bbf0fdef 100644
--- a/services/core/java/com/android/server/wm/SurfaceAnimator.java
+++ b/services/core/java/com/android/server/wm/SurfaceAnimator.java
@@ -57,8 +57,11 @@ class SurfaceAnimator {
@VisibleForTesting
SurfaceControl mLeash;
@VisibleForTesting
+ SurfaceFreezer.Snapshot mSnapshot;
+ @VisibleForTesting
final Animatable mAnimatable;
- private final OnAnimationFinishedCallback mInnerAnimationFinishedCallback;
+ @VisibleForTesting
+ final OnAnimationFinishedCallback mInnerAnimationFinishedCallback;
/**
* Static callback to run on all animations started through this SurfaceAnimator
@@ -151,12 +154,14 @@ class SurfaceAnimator {
* @param animationFinishedCallback The callback being triggered when the animation finishes.
* @param animationCancelledCallback The callback is triggered after the SurfaceAnimator sends a
* cancel call to the underlying AnimationAdapter.
+ * @param snapshotAnim The animation to run for the snapshot. {@code null} if there is no
+ * snapshot.
*/
void startAnimation(Transaction t, AnimationAdapter anim, boolean hidden,
@AnimationType int type,
@Nullable OnAnimationFinishedCallback animationFinishedCallback,
@Nullable Runnable animationCancelledCallback,
- @Nullable SurfaceFreezer freezer) {
+ @Nullable AnimationAdapter snapshotAnim, @Nullable SurfaceFreezer freezer) {
cancelAnimation(t, true /* restarting */, true /* forwardCancel */);
mAnimation = anim;
mAnimationType = type;
@@ -181,12 +186,16 @@ class SurfaceAnimator {
return;
}
mAnimation.startAnimation(mLeash, t, type, mInnerAnimationFinishedCallback);
+ if (snapshotAnim != null) {
+ mSnapshot = freezer.takeSnapshotForAnimation();
+ mSnapshot.startAnimation(t, snapshotAnim, type);
+ }
}
void startAnimation(Transaction t, AnimationAdapter anim, boolean hidden,
@AnimationType int type) {
startAnimation(t, anim, hidden, type, null /* animationFinishedCallback */,
- null /* animationCancelledCallback */, null /* freezer */);
+ null /* animationCancelledCallback */, null /* snapshotAnim */, null /* freezer */);
}
/**
@@ -328,6 +337,7 @@ class SurfaceAnimator {
final OnAnimationFinishedCallback animationFinishedCallback =
mSurfaceAnimationFinishedCallback;
final Runnable animationCancelledCallback = mAnimationCancelledCallback;
+ final SurfaceFreezer.Snapshot snapshot = mSnapshot;
reset(t, false);
if (animation != null) {
if (!mAnimationStartDelayed && forwardCancel) {
@@ -346,9 +356,14 @@ class SurfaceAnimator {
}
}
- if (forwardCancel && leash != null) {
- t.remove(leash);
- mService.scheduleAnimationLocked();
+ if (forwardCancel) {
+ if (snapshot != null) {
+ snapshot.cancelAnimation(t, false /* restarting */);
+ }
+ if (leash != null) {
+ t.remove(leash);
+ mService.scheduleAnimationLocked();
+ }
}
if (!restarting) {
@@ -361,6 +376,12 @@ class SurfaceAnimator {
mAnimation = null;
mSurfaceAnimationFinishedCallback = null;
mAnimationType = ANIMATION_TYPE_NONE;
+ final SurfaceFreezer.Snapshot snapshot = mSnapshot;
+ mSnapshot = null;
+ if (snapshot != null) {
+ // Reset the mSnapshot reference before calling the callback to prevent circular reset.
+ snapshot.cancelAnimation(t, !destroyLeash);
+ }
if (mLeash == null) {
return;
}
@@ -377,11 +398,15 @@ class SurfaceAnimator {
boolean scheduleAnim = false;
final SurfaceControl surface = animatable.getSurfaceControl();
final SurfaceControl parent = animatable.getParentSurfaceControl();
+ final SurfaceControl curAnimationLeash = animatable.getAnimationLeash();
// If the surface was destroyed or the leash is invalid, we don't care to reparent it back.
// Note that we also set this variable to true even if the parent isn't valid anymore, in
// order to ensure onAnimationLeashLost still gets called in this case.
- final boolean reparent = surface != null;
+ // If the animation leash is set, and it is different from the removing leash, it means the
+ // surface now has a new animation surface. We don't want to reparent for that.
+ final boolean reparent = surface != null && (curAnimationLeash == null
+ || curAnimationLeash.equals(leash));
if (reparent) {
if (DEBUG_ANIM) Slog.i(TAG, "Reparenting to original parent: " + parent);
// We shouldn't really need these isValid checks but we do
@@ -608,6 +633,14 @@ class SurfaceAnimator {
void onAnimationLeashLost(Transaction t);
/**
+ * Gets the last created animation leash that has not lost yet.
+ */
+ @Nullable
+ default SurfaceControl getAnimationLeash() {
+ return null;
+ }
+
+ /**
* @return A new surface to be used for the animation leash, inserted at the correct
* position in the hierarchy.
*/
diff --git a/services/core/java/com/android/server/wm/SurfaceFreezer.java b/services/core/java/com/android/server/wm/SurfaceFreezer.java
index 89986cefb207..9a0cabebbdc6 100644
--- a/services/core/java/com/android/server/wm/SurfaceFreezer.java
+++ b/services/core/java/com/android/server/wm/SurfaceFreezer.java
@@ -17,7 +17,6 @@
package com.android.server.wm;
import static com.android.internal.protolog.ProtoLogGroup.WM_SHOW_TRANSACTIONS;
-import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_APP_TRANSITION;
import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_SCREEN_ROTATION;
import android.annotation.NonNull;
@@ -27,13 +26,11 @@ import android.graphics.PixelFormat;
import android.graphics.Point;
import android.graphics.Rect;
import android.hardware.HardwareBuffer;
-import android.view.Surface;
import android.view.SurfaceControl;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.protolog.common.ProtoLog;
-import java.util.function.Supplier;
-
/**
* This class handles "freezing" of an Animatable. The Animatable in question should implement
* Freezable.
@@ -54,7 +51,8 @@ class SurfaceFreezer {
private final Freezable mAnimatable;
private final WindowManagerService mWmService;
- private SurfaceControl mLeash;
+ @VisibleForTesting
+ SurfaceControl mLeash;
Snapshot mSnapshot = null;
final Rect mFreezeBounds = new Rect();
@@ -94,7 +92,7 @@ class SurfaceFreezer {
if (buffer == null || buffer.getWidth() <= 1 || buffer.getHeight() <= 1) {
return;
}
- mSnapshot = new Snapshot(mWmService.mSurfaceFactory, t, screenshotBuffer, mLeash);
+ mSnapshot = new Snapshot(t, screenshotBuffer, mLeash);
}
}
@@ -109,12 +107,25 @@ class SurfaceFreezer {
}
/**
+ * Used by {@link SurfaceAnimator}. This "transfers" the snapshot leash to be used for
+ * animation. By transferring the leash, this will no longer try to clean-up the leash when
+ * finished.
+ */
+ @Nullable
+ Snapshot takeSnapshotForAnimation() {
+ final Snapshot out = mSnapshot;
+ mSnapshot = null;
+ return out;
+ }
+
+ /**
* Clean-up the snapshot and remove leash. If the leash was taken, this just cleans-up the
* snapshot.
*/
void unfreeze(SurfaceControl.Transaction t) {
if (mSnapshot != null) {
mSnapshot.cancelAnimation(t, false /* restarting */);
+ mSnapshot = null;
}
if (mLeash == null) {
return;
@@ -163,13 +174,12 @@ class SurfaceFreezer {
class Snapshot {
private SurfaceControl mSurfaceControl;
private AnimationAdapter mAnimation;
- private SurfaceAnimator.OnAnimationFinishedCallback mFinishedCallback;
/**
* @param t Transaction to create the thumbnail in.
* @param screenshotBuffer A thumbnail or placeholder for thumbnail to initialize with.
*/
- Snapshot(Supplier<Surface> surfaceFactory, SurfaceControl.Transaction t,
+ Snapshot(SurfaceControl.Transaction t,
SurfaceControl.ScreenshotHardwareBuffer screenshotBuffer, SurfaceControl parent) {
// We can't use a delegating constructor since we need to
// reference this::onAnimationFinished
@@ -211,19 +221,15 @@ class SurfaceFreezer {
* component responsible for running the animation. It runs the animation with
* {@link AnimationAdapter#startAnimation} once the hierarchy with
* the Leash has been set up.
- * @param animationFinishedCallback The callback being triggered when the animation
- * finishes.
*/
- void startAnimation(SurfaceControl.Transaction t, AnimationAdapter anim, int type,
- @Nullable SurfaceAnimator.OnAnimationFinishedCallback animationFinishedCallback) {
+ void startAnimation(SurfaceControl.Transaction t, AnimationAdapter anim, int type) {
cancelAnimation(t, true /* restarting */);
mAnimation = anim;
- mFinishedCallback = animationFinishedCallback;
if (mSurfaceControl == null) {
cancelAnimation(t, false /* restarting */);
return;
}
- mAnimation.startAnimation(mSurfaceControl, t, type, animationFinishedCallback);
+ mAnimation.startAnimation(mSurfaceControl, t, type, null /* finishCallback */);
}
/**
@@ -235,18 +241,9 @@ class SurfaceFreezer {
void cancelAnimation(SurfaceControl.Transaction t, boolean restarting) {
final SurfaceControl leash = mSurfaceControl;
final AnimationAdapter animation = mAnimation;
- final SurfaceAnimator.OnAnimationFinishedCallback animationFinishedCallback =
- mFinishedCallback;
mAnimation = null;
- mFinishedCallback = null;
if (animation != null) {
animation.onAnimationCancelled(leash);
- if (!restarting) {
- if (animationFinishedCallback != null) {
- animationFinishedCallback.onAnimationFinished(
- ANIMATION_TYPE_APP_TRANSITION, animation);
- }
- }
}
if (!restarting) {
destroy(t);
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 9a4bf6318249..e90290383a17 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -178,6 +178,10 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
*/
protected final SurfaceAnimator mSurfaceAnimator;
+ /** The parent leash added for animation. */
+ @Nullable
+ private SurfaceControl mAnimationLeash;
+
final SurfaceFreezer mSurfaceFreezer;
protected final WindowManagerService mWmService;
final TransitionController mTransitionController;
@@ -2561,11 +2565,14 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
* @param animationFinishedCallback The callback being triggered when the animation finishes.
* @param animationCancelledCallback The callback is triggered after the SurfaceAnimator sends a
* cancel call to the underlying AnimationAdapter.
+ * @param snapshotAnim The animation to run for the snapshot. {@code null} if there is no
+ * snapshot.
*/
void startAnimation(Transaction t, AnimationAdapter anim, boolean hidden,
@AnimationType int type,
@Nullable OnAnimationFinishedCallback animationFinishedCallback,
- @Nullable Runnable animationCancelledCallback) {
+ @Nullable Runnable animationCancelledCallback,
+ @Nullable AnimationAdapter snapshotAnim) {
if (DEBUG_ANIM) {
Slog.v(TAG, "Starting animation on " + this + ": type=" + type + ", anim=" + anim);
}
@@ -2573,14 +2580,14 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
// TODO: This should use isVisible() but because isVisible has a really weird meaning at
// the moment this doesn't work for all animatable window containers.
mSurfaceAnimator.startAnimation(t, anim, hidden, type, animationFinishedCallback,
- animationCancelledCallback, mSurfaceFreezer);
+ animationCancelledCallback, snapshotAnim, mSurfaceFreezer);
}
void startAnimation(Transaction t, AnimationAdapter anim, boolean hidden,
@AnimationType int type,
@Nullable OnAnimationFinishedCallback animationFinishedCallback) {
startAnimation(t, anim, hidden, type, animationFinishedCallback,
- null /* adapterAnimationCancelledCallback */);
+ null /* adapterAnimationCancelledCallback */, null /* snapshotAnim */);
}
void startAnimation(Transaction t, AnimationAdapter anim, boolean hidden,
@@ -2832,17 +2839,12 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
? taskDisplayArea::clearBackgroundColor : () -> {};
startAnimation(getPendingTransaction(), adapter, !isVisible(),
- ANIMATION_TYPE_APP_TRANSITION,
- (type, anim) -> cleanUpCallback.run(),
- cleanUpCallback);
+ ANIMATION_TYPE_APP_TRANSITION, (type, anim) -> cleanUpCallback.run(),
+ cleanUpCallback, thumbnailAdapter);
if (adapter.getShowWallpaper()) {
getDisplayContent().pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
}
- if (thumbnailAdapter != null) {
- mSurfaceFreezer.mSnapshot.startAnimation(getPendingTransaction(),
- thumbnailAdapter, ANIMATION_TYPE_APP_TRANSITION, (type, anim) -> { });
- }
}
}
@@ -2972,6 +2974,7 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
@Override
public void onAnimationLeashCreated(Transaction t, SurfaceControl leash) {
mLastLayer = -1;
+ mAnimationLeash = leash;
reassignLayer(t);
// Leash is now responsible for position, so set our position to 0.
@@ -2981,11 +2984,16 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
@Override
public void onAnimationLeashLost(Transaction t) {
mLastLayer = -1;
- mSurfaceFreezer.unfreeze(t);
+ mAnimationLeash = null;
reassignLayer(t);
updateSurfacePosition(t);
}
+ @Override
+ public SurfaceControl getAnimationLeash() {
+ return mAnimationLeash;
+ }
+
private void doAnimationFinished(@AnimationType int type, AnimationAdapter anim) {
for (int i = 0; i < mSurfaceAnimationSources.size(); ++i) {
mSurfaceAnimationSources.valueAt(i).onAnimationFinished(type, anim);
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
index 68053eb324b2..bbeb980353ed 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
@@ -25,6 +25,7 @@ import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
import static android.view.WindowManager.TRANSIT_CLOSE;
import static android.view.WindowManager.TRANSIT_OLD_TASK_CLOSE;
+import static android.view.WindowManager.TRANSIT_OLD_TASK_FRAGMENT_CHANGE;
import static android.view.WindowManager.TRANSIT_OLD_TASK_OPEN;
import static android.view.WindowManager.TRANSIT_OPEN;
import static android.window.DisplayAreaOrganizer.FEATURE_DEFAULT_TASK_CONTAINER;
@@ -52,6 +53,7 @@ import static com.android.server.wm.WindowContainer.POSITION_TOP;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
@@ -1106,6 +1108,71 @@ public class WindowContainerTests extends WindowTestsBase {
verify(surfaceAnimator, never()).setRelativeLayer(any(), any(), anyInt());
}
+ @Test
+ public void testStartChangeTransitionWhenPreviousIsNotFinished() {
+ final WindowContainer container = createTaskFragmentWithParentTask(
+ createTask(mDisplayContent), false);
+ container.mSurfaceControl = mock(SurfaceControl.class);
+ final SurfaceAnimator surfaceAnimator = container.mSurfaceAnimator;
+ final SurfaceFreezer surfaceFreezer = container.mSurfaceFreezer;
+ final SurfaceControl.Transaction t = mock(SurfaceControl.Transaction.class);
+ spyOn(container);
+ spyOn(surfaceAnimator);
+ spyOn(surfaceFreezer);
+ doReturn(t).when(container).getPendingTransaction();
+ doReturn(t).when(container).getSyncTransaction();
+
+ // Leash and snapshot created for change transition.
+ container.initializeChangeTransition(new Rect(0, 0, 1000, 2000));
+ // Can't really take a snapshot, manually set one.
+ surfaceFreezer.mSnapshot = mock(SurfaceFreezer.Snapshot.class);
+
+ assertNotNull(surfaceFreezer.mLeash);
+ assertEquals(surfaceFreezer.mLeash, container.getAnimationLeash());
+
+ // Start animation: surfaceAnimator take over the leash and snapshot from surfaceFreezer.
+ container.applyAnimationUnchecked(null /* lp */, true /* enter */,
+ TRANSIT_OLD_TASK_FRAGMENT_CHANGE, false /* isVoiceInteraction */,
+ null /* sources */);
+
+ assertNull(surfaceFreezer.mLeash);
+ assertNull(surfaceFreezer.mSnapshot);
+ assertNotNull(surfaceAnimator.mLeash);
+ assertNotNull(surfaceAnimator.mSnapshot);
+ final SurfaceControl prevLeash = surfaceAnimator.mLeash;
+ final SurfaceFreezer.Snapshot prevSnapshot = surfaceAnimator.mSnapshot;
+
+ // Prepare another change transition.
+ container.initializeChangeTransition(new Rect(0, 0, 1000, 2000));
+ surfaceFreezer.mSnapshot = mock(SurfaceFreezer.Snapshot.class);
+
+ assertNotNull(surfaceFreezer.mLeash);
+ assertEquals(surfaceFreezer.mLeash, container.getAnimationLeash());
+ assertNotEquals(prevLeash, container.getAnimationLeash());
+
+ // Start another animation before the previous one is finished, it should reset the previous
+ // one, but not change the current one.
+ container.applyAnimationUnchecked(null /* lp */, true /* enter */,
+ TRANSIT_OLD_TASK_FRAGMENT_CHANGE, false /* isVoiceInteraction */,
+ null /* sources */);
+
+ verify(container, never()).onAnimationLeashLost(any());
+ verify(surfaceFreezer, never()).unfreeze(any());
+ assertNotNull(surfaceAnimator.mLeash);
+ assertNotNull(surfaceAnimator.mSnapshot);
+ assertEquals(surfaceAnimator.mLeash, container.getAnimationLeash());
+ assertNotEquals(prevLeash, surfaceAnimator.mLeash);
+ assertNotEquals(prevSnapshot, surfaceAnimator.mSnapshot);
+
+ // Clean up after animation finished.
+ surfaceAnimator.mInnerAnimationFinishedCallback.onAnimationFinished(
+ ANIMATION_TYPE_APP_TRANSITION, surfaceAnimator.getAnimation());
+
+ verify(container).onAnimationLeashLost(any());
+ assertNull(surfaceAnimator.mLeash);
+ assertNull(surfaceAnimator.mSnapshot);
+ }
+
/* Used so we can gain access to some protected members of the {@link WindowContainer} class */
private static class TestWindowContainer extends WindowContainer<TestWindowContainer> {
private final int mLayer;