Prevents recursive call into end() or cancel()

end() and cancel() in ValueAnimator will trigger onAnimationCancel
or onAnimationEnd callback on its listeners. If additional end()
or cancel() request comes from the callback, a loop will be formed.
Therefore, we need to mark when the end is reuqested so we do not
process end() or cancel() request multiple times during one animation
run, and also effectively terminate the loop.

Bug: 23596652
Change-Id: Iefb69eb8969071b43124c09d7cccbe9ff5ba5dcc
diff --git a/core/java/android/animation/ValueAnimator.java b/core/java/android/animation/ValueAnimator.java
index 065631c..da9a8c8 100644
--- a/core/java/android/animation/ValueAnimator.java
+++ b/core/java/android/animation/ValueAnimator.java
@@ -185,6 +185,11 @@
      */
     boolean mInitialized = false;
 
+    /**
+     * Flag that tracks whether animation has been requested to end.
+     */
+    private boolean mAnimationEndRequested = false;
+
     //
     // Backing variables
     //
@@ -915,6 +920,7 @@
         mStarted = true;
         mPaused = false;
         mRunning = false;
+        mAnimationEndRequested = false;
         updateScaledDuration(); // in case the scale factor has changed since creation time
         AnimationHandler animationHandler = AnimationHandler.getInstance();
         animationHandler.addAnimationFrameCallback(this, mStartDelay);
@@ -930,9 +936,15 @@
         if (Looper.myLooper() == null) {
             throw new AndroidRuntimeException("Animators may only be run on Looper threads");
         }
+
+        // If end has already been requested, through a previous end() or cancel() call, no-op
+        // until animation starts again.
+        if (mAnimationEndRequested) {
+            return;
+        }
+
         // Only cancel if the animation is actually running or has been started and is about
         // to run
-
         // Only notify listeners if the animator has actually started
         if ((mStarted || mRunning) && mListeners != null) {
             if (!mRunning) {
@@ -1029,9 +1041,14 @@
      * Called internally to end an animation by removing it from the animations list. Must be
      * called on the UI thread.
      */
-     private void endAnimation() {
+    private void endAnimation() {
+        if (mAnimationEndRequested) {
+            return;
+        }
         AnimationHandler handler = AnimationHandler.getInstance();
         handler.removeCallback(this);
+
+        mAnimationEndRequested = true;
         mPaused = false;
         if ((mStarted || mRunning) && mListeners != null) {
             if (!mRunning) {
@@ -1256,6 +1273,7 @@
         anim.mStartListenersCalled = false;
         anim.mStartTime = 0;
         anim.mStartTimeCommitted = false;
+        anim.mAnimationEndRequested = false;
         anim.mPauseTime = 0;
         anim.mLastFrameTime = 0;
         anim.mCurrentFraction = 0;