Lock free app animations (3/n): Implement transfering animations
Test: go/wm-smoke
Bug: 64674361
Change-Id: I2170c31c1a95cea049bcc66978bb7737337503b3
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index da144e6..dcf2a87 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -1126,7 +1126,12 @@
}
setClientHidden(fromToken.mClientHidden);
- // TODO: Transfer animation
+ transferAnimation(fromToken);
+
+ // When transferring an animation, we no longer need to apply an animation to the
+ // the token we transfer the animation over. Thus, remove the animation from
+ // pending opening apps.
+ mService.mOpeningApps.remove(this);
mService.updateFocusedWindowLocked(
UPDATE_FOCUS_WILL_PLACE_SURFACES, true /*updateInputWindows*/);
diff --git a/services/core/java/com/android/server/wm/SurfaceAnimationRunner.java b/services/core/java/com/android/server/wm/SurfaceAnimationRunner.java
index 3ef9b3f..a63742e 100644
--- a/services/core/java/com/android/server/wm/SurfaceAnimationRunner.java
+++ b/services/core/java/com/android/server/wm/SurfaceAnimationRunner.java
@@ -158,6 +158,7 @@
mRunningAnimations.remove(a.mLeash);
synchronized (mCancelLock) {
if (!a.mCancelled) {
+
// Post on other thread that we can push final state without jank.
AnimationThread.getHandler().post(a.mFinishCallback);
}
diff --git a/services/core/java/com/android/server/wm/SurfaceAnimator.java b/services/core/java/com/android/server/wm/SurfaceAnimator.java
index e165211..bda5bc9 100644
--- a/services/core/java/com/android/server/wm/SurfaceAnimator.java
+++ b/services/core/java/com/android/server/wm/SurfaceAnimator.java
@@ -22,10 +22,13 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.util.ArrayMap;
import android.util.Slog;
import android.view.SurfaceControl;
import android.view.SurfaceControl.Transaction;
+import com.android.internal.annotations.VisibleForTesting;
+
import java.io.PrintWriter;
/**
@@ -41,7 +44,9 @@
private static final String TAG = TAG_WITH_CLASS_NAME ? "SurfaceAnimator" : TAG_WM;
private final WindowManagerService mService;
private AnimationAdapter mAnimation;
- private SurfaceControl mLeash;
+
+ @VisibleForTesting
+ SurfaceControl mLeash;
private final Animatable mAnimatable;
private final OnAnimationFinishedCallback mInnerAnimationFinishedCallback;
private final Runnable mAnimationFinishedCallback;
@@ -62,6 +67,11 @@
private OnAnimationFinishedCallback getFinishedCallback(Runnable animationFinishedCallback) {
return anim -> {
synchronized (mService.mWindowMap) {
+ final SurfaceAnimator target = mService.mAnimationTransferMap.remove(anim);
+ if (target != null) {
+ target.mInnerAnimationFinishedCallback.onAnimationFinished(anim);
+ return;
+ }
if (anim != mAnimation) {
// Callback was from another animation - ignore.
return;
@@ -70,7 +80,7 @@
final Transaction t = new Transaction();
SurfaceControl.openTransaction();
try {
- reset(t);
+ reset(t, true /* destroyLeash */);
animationFinishedCallback.run();
} finally {
// TODO: This should use pendingTransaction eventually, but right now things
@@ -95,7 +105,7 @@
* handing it to the component that is responsible to run the animation.
*/
void startAnimation(Transaction t, AnimationAdapter anim, boolean hidden) {
- cancelAnimation(t, true /* restarting */);
+ cancelAnimation(t, true /* restarting */, true /* forwardCancel */);
mAnimation = anim;
final SurfaceControl surface = mAnimatable.getSurfaceControl();
if (surface == null) {
@@ -158,7 +168,8 @@
* Cancels any currently running animation.
*/
void cancelAnimation() {
- cancelAnimation(mAnimatable.getPendingTransaction(), false /* restarting */);
+ cancelAnimation(mAnimatable.getPendingTransaction(), false /* restarting */,
+ true /* forwardCancel */);
mAnimatable.commitPendingTransaction();
}
@@ -197,13 +208,47 @@
return mLeash != null;
}
- private void cancelAnimation(Transaction t, boolean restarting) {
+ void transferAnimation(SurfaceAnimator from) {
+ if (from.mLeash == null) {
+ return;
+ }
+ final SurfaceControl surface = mAnimatable.getSurfaceControl();
+ final SurfaceControl parent = mAnimatable.getParentSurfaceControl();
+ if (surface == null || parent == null) {
+ Slog.w(TAG, "Unable to transfer animation, surface or parent is null");
+ cancelAnimation();
+ return;
+ }
+ endDelayingAnimationStart();
+ final Transaction t = mAnimatable.getPendingTransaction();
+ cancelAnimation(t, true /* restarting */, true /* forwardCancel */);
+ mLeash = from.mLeash;
+ mAnimation = from.mAnimation;
+
+ // Cancel source animation, but don't let animation runner cancel the animation.
+ from.cancelAnimation(t, false /* restarting */, false /* forwardCancel */);
+ t.reparent(surface, mLeash.getHandle());
+ t.reparent(mLeash, parent.getHandle());
+ mAnimatable.onAnimationLeashCreated(t, mLeash);
+ mService.mAnimationTransferMap.put(mAnimation, this);
+ }
+
+ /**
+ * Cancels the animation, and resets the leash.
+ *
+ * @param t The transaction to use for all cancelling surface operations.
+ * @param restarting Whether we are restarting the animation.
+ * @param forwardCancel Whether to forward the cancel signal to the adapter executing the
+ * animation. This will be set to false when just transferring an animation
+ * to another animator.
+ */
+ private void cancelAnimation(Transaction t, boolean restarting, boolean forwardCancel) {
if (DEBUG_ANIM) Slog.i(TAG, "Cancelling animation restarting=" + restarting);
final SurfaceControl leash = mLeash;
final AnimationAdapter animation = mAnimation;
- reset(t);
+ reset(t, forwardCancel);
if (animation != null) {
- if (!mAnimationStartDelayed) {
+ if (!mAnimationStartDelayed && forwardCancel) {
animation.onAnimationCancelled(leash);
}
if (!restarting) {
@@ -215,7 +260,7 @@
}
}
- private void reset(Transaction t) {
+ private void reset(Transaction t, boolean destroyLeash) {
final SurfaceControl surface = mAnimatable.getSurfaceControl();
final SurfaceControl parent = mAnimatable.getParentSurfaceControl();
@@ -225,7 +270,8 @@
if (DEBUG_ANIM) Slog.i(TAG, "Reparenting to original parent");
t.reparent(surface, parent.getHandle());
}
- if (mLeash != null) {
+ mService.mAnimationTransferMap.remove(mAnimation);
+ if (mLeash != null && destroyLeash) {
mAnimatable.destroyAfterPendingTransaction(mLeash);
}
mLeash = null;
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index a0c704e..af06900 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -997,6 +997,10 @@
mSurfaceAnimator.startAnimation(t, anim, hidden);
}
+ void transferAnimation(WindowContainer from) {
+ mSurfaceAnimator.transferAnimation(from.mSurfaceAnimator);
+ }
+
void cancelAnimation() {
mSurfaceAnimator.cancelAnimation();
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 9678826..6e2dbe1 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -177,6 +177,7 @@
import android.os.WorkSource;
import android.provider.Settings;
import android.text.format.DateUtils;
+import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.DisplayMetrics;
import android.util.EventLog;
@@ -767,6 +768,11 @@
final WindowAnimator mAnimator;
final SurfaceAnimationRunner mSurfaceAnimationRunner;
+ /**
+ * Keeps track of which animations got transferred to which animators. Entries will get cleaned
+ * up when the animation finishes.
+ */
+ final ArrayMap<AnimationAdapter, SurfaceAnimator> mAnimationTransferMap = new ArrayMap<>();
final BoundsAnimationController mBoundsAnimationController;
private final PointerEventDispatcher mPointerEventDispatcher;