diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
index d08c573736d14967eb7191d194cacbbc8f406cb8..cb5d1c42c7d295938ffb898fe8469593457a9b83 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
@@ -83,11 +83,6 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
     public static final boolean IS_ENABLED =
             SystemProperties.getInt("persist.wm.debug.predictive_back",
                     SETTING_VALUE_ON) == SETTING_VALUE_ON;
-     /** Flag for U animation features */
-    public static boolean IS_U_ANIMATION_ENABLED =
-            SystemProperties.getInt("persist.wm.debug.predictive_back_anim",
-                    SETTING_VALUE_ON) == SETTING_VALUE_ON;
-
     public static final float FLING_MAX_LENGTH_SECONDS = 0.1f;     // 100ms
     public static final float FLING_SPEED_UP_FACTOR = 0.6f;
 
@@ -110,10 +105,9 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
 
     /** Tracks if an uninterruptible animation is in progress */
     private boolean mPostCommitAnimationInProgress = false;
+
     /** Tracks if we should start the back gesture on the next motion move event */
     private boolean mShouldStartOnNextMoveEvent = false;
-    /** @see #setTriggerBack(boolean) */
-    private boolean mTriggerBack;
 
     private final FlingAnimationUtils mFlingAnimationUtils;
 
@@ -128,6 +122,18 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
     private final ShellController mShellController;
     private final ShellExecutor mShellExecutor;
     private final Handler mBgHandler;
+
+    /**
+     * Tracks the current user back gesture.
+     */
+    private TouchTracker mCurrentTracker = new TouchTracker();
+
+    /**
+     * Tracks the next back gesture in case a new user gesture has started while the back animation
+     * (and navigation) associated with {@link #mCurrentTracker} have not yet finished.
+     */
+    private TouchTracker mQueuedTracker = new TouchTracker();
+
     private final Runnable mAnimationTimeoutRunnable = () -> {
         ProtoLog.w(WM_SHELL_BACK_PREVIEW, "Animation didn't finish in %d ms. Resetting...",
                 MAX_ANIMATION_DURATION);
@@ -138,8 +144,6 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
     @VisibleForTesting
     BackAnimationAdapter mBackAnimationAdapter;
 
-    private final TouchTracker mTouchTracker = new TouchTracker();
-
     @Nullable
     private IOnBackInvokedCallback mActiveCallback;
 
@@ -156,7 +160,7 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
                         }
                         ProtoLog.i(WM_SHELL_BACK_PREVIEW, "Navigation window gone.");
                         setTriggerBack(false);
-                        onGestureFinished(false);
+                        resetTouchTracker();
                     });
                 }
             });
@@ -357,6 +361,12 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
         mShellBackAnimationRegistry.unregisterAnimation(type);
     }
 
+    private TouchTracker getActiveTracker() {
+        if (mCurrentTracker.isActive()) return mCurrentTracker;
+        if (mQueuedTracker.isActive()) return mQueuedTracker;
+        return null;
+    }
+
     /**
      * Called when a new motion event needs to be transferred to this
      * {@link BackAnimationController}
@@ -368,11 +378,18 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
             float velocityY,
             int keyAction,
             @BackEvent.SwipeEdge int swipeEdge) {
-        if (mPostCommitAnimationInProgress) {
+
+        TouchTracker activeTouchTracker = getActiveTracker();
+        if (activeTouchTracker != null) {
+            activeTouchTracker.update(touchX, touchY, velocityX, velocityY);
+        }
+
+        // two gestures are waiting to be processed at the moment, skip any further user touches
+        if (mCurrentTracker.isFinished() && mQueuedTracker.isFinished()) {
+            Log.d(TAG, "Ignoring MotionEvent because two gestures are already being queued.");
             return;
         }
 
-        mTouchTracker.update(touchX, touchY, velocityX, velocityY);
         if (keyAction == MotionEvent.ACTION_DOWN) {
             if (!mBackGestureStarted) {
                 mShouldStartOnNextMoveEvent = true;
@@ -390,33 +407,46 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
             ProtoLog.d(WM_SHELL_BACK_PREVIEW,
                     "Finishing gesture with event action: %d", keyAction);
             if (keyAction == MotionEvent.ACTION_CANCEL) {
-                mTriggerBack = false;
+                setTriggerBack(false);
             }
-            onGestureFinished(true);
+            onGestureFinished();
         }
     }
 
     private void onGestureStarted(float touchX, float touchY, @BackEvent.SwipeEdge int swipeEdge) {
-        ProtoLog.d(WM_SHELL_BACK_PREVIEW, "initAnimation mMotionStarted=%b", mBackGestureStarted);
-        if (mBackGestureStarted || mBackNavigationInfo != null) {
-            Log.e(TAG, "Animation is being initialized but is already started.");
-            finishBackNavigation();
+        TouchTracker touchTracker;
+        if (mCurrentTracker.isInInitialState()) {
+            touchTracker = mCurrentTracker;
+        } else if (mQueuedTracker.isInInitialState()) {
+            touchTracker = mQueuedTracker;
+        } else {
+            Log.w(TAG, "Cannot start tracking new gesture with neither tracker in initial state.");
+            return;
         }
-
-        mTouchTracker.setGestureStartLocation(touchX, touchY, swipeEdge);
+        touchTracker.setGestureStartLocation(touchX, touchY, swipeEdge);
+        touchTracker.setState(TouchTracker.TouchTrackerState.ACTIVE);
         mBackGestureStarted = true;
 
+        if (touchTracker == mCurrentTracker) {
+            // Only start the back navigation if no other gesture is being processed. Otherwise,
+            // the back navigation will be started once the current gesture has finished.
+            startBackNavigation(mCurrentTracker);
+        }
+    }
+
+    private void startBackNavigation(@NonNull TouchTracker touchTracker) {
         try {
             mBackNavigationInfo = mActivityTaskManager.startBackNavigation(
                     mNavigationObserver, mEnableAnimations.get() ? mBackAnimationAdapter : null);
-            onBackNavigationInfoReceived(mBackNavigationInfo);
+            onBackNavigationInfoReceived(mBackNavigationInfo, touchTracker);
         } catch (RemoteException remoteException) {
             Log.e(TAG, "Failed to initAnimation", remoteException);
-            finishBackNavigation();
+            finishBackNavigation(touchTracker.getTriggerBack());
         }
     }
 
-    private void onBackNavigationInfoReceived(@Nullable BackNavigationInfo backNavigationInfo) {
+    private void onBackNavigationInfoReceived(@Nullable BackNavigationInfo backNavigationInfo,
+            @NonNull TouchTracker touchTracker) {
         ProtoLog.d(WM_SHELL_BACK_PREVIEW, "Received backNavigationInfo:%s", backNavigationInfo);
         if (backNavigationInfo == null) {
             Log.e(TAG, "Received BackNavigationInfo is null.");
@@ -430,7 +460,7 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
             }
         } else {
             mActiveCallback = mBackNavigationInfo.getOnBackInvokedCallback();
-            dispatchOnBackStarted(mActiveCallback, mTouchTracker.createStartEvent(null));
+            dispatchOnBackStarted(mActiveCallback, touchTracker.createStartEvent(null));
         }
     }
 
@@ -438,12 +468,14 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
         if (!mBackGestureStarted || mBackNavigationInfo == null || mActiveCallback == null) {
             return;
         }
-
-        final BackMotionEvent backEvent = mTouchTracker.createProgressEvent();
+        // Skip dispatching if the move corresponds to the queued instead of the current gesture
+        if (mQueuedTracker.isActive()) return;
+        final BackMotionEvent backEvent = mCurrentTracker.createProgressEvent();
         dispatchOnBackProgressed(mActiveCallback, backEvent);
     }
 
     private void injectBackKey() {
+        Log.d(TAG, "injectBackKey");
         sendBackEvent(KeyEvent.ACTION_DOWN);
         sendBackEvent(KeyEvent.ACTION_UP);
     }
@@ -488,7 +520,8 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
      *
      * @param callback the callback to be invoked when the animation ends.
      */
-    private void dispatchOrAnimateOnBackInvoked(IOnBackInvokedCallback callback) {
+    private void dispatchOrAnimateOnBackInvoked(IOnBackInvokedCallback callback,
+            @NonNull TouchTracker touchTracker) {
         if (callback == null) {
             return;
         }
@@ -497,12 +530,12 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
 
         if (mBackNavigationInfo != null && mBackNavigationInfo.isAnimationCallback()) {
 
-            final BackMotionEvent backMotionEvent = mTouchTracker.createProgressEvent();
+            final BackMotionEvent backMotionEvent = touchTracker.createProgressEvent();
             if (backMotionEvent != null) {
                 // Constraints - absolute values
                 float minVelocity = mFlingAnimationUtils.getMinVelocityPxPerSecond();
                 float maxVelocity = mFlingAnimationUtils.getHighVelocityPxPerSecond();
-                float maxX = mTouchTracker.getMaxDistance(); // px
+                float maxX = touchTracker.getMaxDistance(); // px
                 float maxFlingDistance = maxX * MAX_FLING_PROGRESS; // px
 
                 // Current state
@@ -530,9 +563,9 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
 
                     animator.addUpdateListener(animation -> {
                         Float animatedValue = (Float) animation.getAnimatedValue();
-                        float progress = mTouchTracker.getProgress(animatedValue);
-                        final BackMotionEvent backEvent = mTouchTracker
-                                .createProgressEvent(progress);
+                        float progress = touchTracker.getProgress(animatedValue);
+                        final BackMotionEvent backEvent = touchTracker.createProgressEvent(
+                                progress);
                         dispatchOnBackProgressed(mActiveCallback, backEvent);
                     });
 
@@ -591,27 +624,27 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
      * Sets to true when the back gesture has passed the triggering threshold, false otherwise.
      */
     public void setTriggerBack(boolean triggerBack) {
-        if (mPostCommitAnimationInProgress) {
-            return;
+        TouchTracker activeBackGestureInfo = getActiveTracker();
+        if (activeBackGestureInfo != null) {
+            activeBackGestureInfo.setTriggerBack(triggerBack);
         }
-        mTriggerBack = triggerBack;
-        mTouchTracker.setTriggerBack(triggerBack);
     }
 
     private void setSwipeThresholds(
             float linearDistance,
             float maxDistance,
             float nonLinearFactor) {
-        mTouchTracker.setProgressThresholds(linearDistance, maxDistance, nonLinearFactor);
+        mCurrentTracker.setProgressThresholds(linearDistance, maxDistance, nonLinearFactor);
+        mQueuedTracker.setProgressThresholds(linearDistance, maxDistance, nonLinearFactor);
     }
 
-    private void invokeOrCancelBack() {
+    private void invokeOrCancelBack(@NonNull TouchTracker touchTracker) {
         // Make a synchronized call to core before dispatch back event to client side.
         // If the close transition happens before the core receives onAnimationFinished, there will
         // play a second close animation for that transition.
         if (mBackAnimationFinishedCallback != null) {
             try {
-                mBackAnimationFinishedCallback.onAnimationFinished(mTriggerBack);
+                mBackAnimationFinishedCallback.onAnimationFinished(touchTracker.getTriggerBack());
             } catch (RemoteException e) {
                 Log.e(TAG, "Failed call IBackAnimationFinishedCallback", e);
             }
@@ -620,30 +653,30 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
 
         if (mBackNavigationInfo != null) {
             final IOnBackInvokedCallback callback = mBackNavigationInfo.getOnBackInvokedCallback();
-            if (mTriggerBack) {
-                dispatchOrAnimateOnBackInvoked(callback);
+            if (touchTracker.getTriggerBack()) {
+                dispatchOrAnimateOnBackInvoked(callback, touchTracker);
             } else {
                 dispatchOnBackCancelled(callback);
             }
         }
-        finishBackNavigation();
+        finishBackNavigation(touchTracker.getTriggerBack());
     }
 
     /**
      * Called when the gesture is released, then it could start the post commit animation.
      */
-    private void onGestureFinished(boolean fromTouch) {
-        ProtoLog.d(WM_SHELL_BACK_PREVIEW, "onGestureFinished() mTriggerBack == %s", mTriggerBack);
-        if (!mBackGestureStarted) {
-            finishBackNavigation();
+    private void onGestureFinished() {
+        TouchTracker activeTouchTracker = getActiveTracker();
+        if (!mBackGestureStarted || activeTouchTracker == null) {
+            // This can happen when an unfinished gesture has been reset in resetTouchTracker
+            Log.d(TAG, "onGestureFinished called while no gesture is started");
             return;
         }
+        boolean triggerBack = activeTouchTracker.getTriggerBack();
+        ProtoLog.d(WM_SHELL_BACK_PREVIEW, "onGestureFinished() mTriggerBack == %s", triggerBack);
 
-        if (fromTouch) {
-            // Let touch reset the flag otherwise it will start a new back navigation and refresh
-            // the info when received a new move event.
-            mBackGestureStarted = false;
-        }
+        mBackGestureStarted = false;
+        activeTouchTracker.setState(TouchTracker.TouchTrackerState.FINISHED);
 
         if (mPostCommitAnimationInProgress) {
             ProtoLog.w(WM_SHELL_BACK_PREVIEW, "Animation is still running");
@@ -652,11 +685,16 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
 
         if (mBackNavigationInfo == null) {
             // No focus window found or core are running recents animation, inject back key as
-            // legacy behavior.
-            if (mTriggerBack) {
+            // legacy behavior, or new back gesture was started while previous has not finished yet
+            if (!mQueuedTracker.isInInitialState()) {
+                Log.e(TAG, "mBackNavigationInfo is null AND there is another back animation in "
+                        + "progress");
+            }
+            mCurrentTracker.reset();
+            if (triggerBack) {
                 injectBackKey();
             }
-            finishBackNavigation();
+            finishBackNavigation(triggerBack);
             return;
         }
 
@@ -664,7 +702,9 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
         // Simply trigger and finish back navigation when no animator defined.
         if (!shouldDispatchToAnimator()
                 || mShellBackAnimationRegistry.isAnimationCancelledOrNull(backType)) {
-            invokeOrCancelBack();
+            Log.d(TAG, "Trigger back without dispatching to animator.");
+            invokeOrCancelBack(mCurrentTracker);
+            mCurrentTracker.reset();
             return;
         } else if (mShellBackAnimationRegistry.isWaitingAnimation(backType)) {
             ProtoLog.w(WM_SHELL_BACK_PREVIEW, "Gesture released, but animation didn't ready.");
@@ -691,8 +731,8 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
         mShellExecutor.executeDelayed(mAnimationTimeoutRunnable, MAX_ANIMATION_DURATION);
 
         // The next callback should be {@link #onBackAnimationFinished}.
-        if (mTriggerBack) {
-            dispatchOrAnimateOnBackInvoked(mActiveCallback);
+        if (mCurrentTracker.getTriggerBack()) {
+            dispatchOrAnimateOnBackInvoked(mActiveCallback, mCurrentTracker);
         } else {
             dispatchOnBackCancelled(mActiveCallback);
         }
@@ -708,27 +748,64 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
         mShellExecutor.removeCallbacks(mAnimationTimeoutRunnable);
         mPostCommitAnimationInProgress = false;
 
-        ProtoLog.d(WM_SHELL_BACK_PREVIEW, "BackAnimationController: onBackAnimationFinished()");
+        Log.d(TAG, "BackAnimationController: onBackAnimationFinished()");
+
+        if (mCurrentTracker.isActive() || mCurrentTracker.isFinished()) {
+            // Trigger the real back.
+            invokeOrCancelBack(mCurrentTracker);
+        } else {
+            Log.d(TAG, "mCurrentBackGestureInfo was null when back animation finished");
+        }
+        resetTouchTracker();
+    }
+
+    /**
+     * Resets the TouchTracker and potentially starts a new back navigation in case one is queued
+     */
+    private void resetTouchTracker() {
+        TouchTracker temp = mCurrentTracker;
+        mCurrentTracker = mQueuedTracker;
+        temp.reset();
+        mQueuedTracker = temp;
+
+        if (mCurrentTracker.isInInitialState()) {
+            if (mBackGestureStarted) {
+                mBackGestureStarted = false;
+                dispatchOnBackCancelled(mActiveCallback);
+                finishBackNavigation(false);
+                Log.d(TAG, "resetTouchTracker -> reset an unfinished gesture");
+            } else {
+                Log.d(TAG, "resetTouchTracker -> no queued gesture");
+            }
+            return;
+        }
 
-        // Trigger the real back.
-        invokeOrCancelBack();
+        if (mCurrentTracker.isFinished() && mCurrentTracker.getTriggerBack()) {
+            Log.d(TAG, "resetTouchTracker -> start queued back navigation AND post commit "
+                    + "animation");
+            injectBackKey();
+            finishBackNavigation(true);
+            mCurrentTracker.reset();
+        } else if (!mCurrentTracker.isFinished()) {
+            Log.d(TAG, "resetTouchTracker -> queued gesture not finished; do nothing");
+        } else {
+            Log.d(TAG, "resetTouchTracker -> reset queued gesture");
+            mCurrentTracker.reset();
+        }
     }
 
     /**
      * This should be called after the whole back navigation is completed.
      */
     @VisibleForTesting
-    void finishBackNavigation() {
+    void finishBackNavigation(boolean triggerBack) {
         ProtoLog.d(WM_SHELL_BACK_PREVIEW, "BackAnimationController: finishBackNavigation()");
-        mShouldStartOnNextMoveEvent = false;
-        mTouchTracker.reset();
         mActiveCallback = null;
         mShellBackAnimationRegistry.resetDefaultCrossActivity();
         if (mBackNavigationInfo != null) {
-            mBackNavigationInfo.onBackNavigationFinished(mTriggerBack);
+            mBackNavigationInfo.onBackNavigationFinished(triggerBack);
             mBackNavigationInfo = null;
         }
-        mTriggerBack = false;
     }
 
 
@@ -781,14 +858,14 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
                                     if (apps.length >= 1) {
                                         dispatchOnBackStarted(
                                                 mActiveCallback,
-                                                mTouchTracker.createStartEvent(apps[0]));
+                                                mCurrentTracker.createStartEvent(apps[0]));
                                     }
 
                                     // Dispatch the first progress after animation start for
                                     // smoothing the initial animation, instead of waiting for next
                                     // onMove.
-                                    final BackMotionEvent backFinish =
-                                            mTouchTracker.createProgressEvent();
+                                    final BackMotionEvent backFinish = mCurrentTracker
+                                            .createProgressEvent();
                                     dispatchOnBackProgressed(mActiveCallback, backFinish);
                                     if (!mBackGestureStarted) {
                                         // if the down -> up gesture happened before animation
@@ -808,7 +885,7 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
                                         return;
                                     }
                                     if (!mBackGestureStarted) {
-                                        invokeOrCancelBack();
+                                        invokeOrCancelBack(mCurrentTracker);
                                     }
                                 });
                     }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/TouchTracker.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/TouchTracker.java
index a0ada39b459e4be3cab29b1cb6be7027571bd8f6..8a59a9f62425004489187a04c284fee8e4d0e182 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/TouchTracker.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/TouchTracker.java
@@ -52,6 +52,7 @@ class TouchTracker {
     private float mStartThresholdX;
     private int mSwipeEdge;
     private boolean mCancelled;
+    private TouchTrackerState mState = TouchTrackerState.INITIAL;
 
     void update(float touchX, float touchY, float velocityX, float velocityY) {
         /**
@@ -76,6 +77,26 @@ class TouchTracker {
         mTriggerBack = triggerBack;
     }
 
+    boolean getTriggerBack() {
+        return mTriggerBack;
+    }
+
+    void setState(TouchTrackerState state) {
+        mState = state;
+    }
+
+    boolean isInInitialState() {
+        return mState == TouchTrackerState.INITIAL;
+    }
+
+    boolean isActive() {
+        return mState == TouchTrackerState.ACTIVE;
+    }
+
+    boolean isFinished() {
+        return mState == TouchTrackerState.FINISHED;
+    }
+
     void setGestureStartLocation(float touchX, float touchY, int swipeEdge) {
         mInitTouchX = touchX;
         mInitTouchY = touchY;
@@ -89,6 +110,7 @@ class TouchTracker {
         mStartThresholdX = 0;
         mCancelled = false;
         mTriggerBack = false;
+        mState = TouchTrackerState.INITIAL;
         mSwipeEdge = BackEvent.EDGE_LEFT;
     }
 
@@ -186,4 +208,9 @@ class TouchTracker {
         mMaxDistance = maxDistance;
         mNonLinearFactor = nonLinearFactor;
     }
+
+    enum TouchTrackerState {
+        INITIAL, ACTIVE, FINISHED
+    }
+
 }
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java
index d6141cfd21baf1c08750129a550c826349e6b0f9..6cf5450db9ea064289f5a7536c33a9a3b7bf1405 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java
@@ -37,6 +37,7 @@ import android.app.WindowConfiguration;
 import android.content.pm.ApplicationInfo;
 import android.graphics.Point;
 import android.graphics.Rect;
+import android.hardware.input.InputManager;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.RemoteCallback;
@@ -44,9 +45,9 @@ import android.os.RemoteException;
 import android.provider.Settings;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableContentResolver;
-import android.testing.TestableContext;
 import android.testing.TestableLooper;
 import android.view.IRemoteAnimationRunner;
+import android.view.KeyEvent;
 import android.view.MotionEvent;
 import android.view.RemoteAnimationTarget;
 import android.view.SurfaceControl;
@@ -58,7 +59,6 @@ import android.window.IOnBackInvokedCallback;
 
 import androidx.annotation.Nullable;
 import androidx.test.filters.SmallTest;
-import androidx.test.platform.app.InstrumentationRegistry;
 
 import com.android.internal.util.test.FakeSettingsProvider;
 import com.android.wm.shell.ShellTestCase;
@@ -69,7 +69,6 @@ import com.android.wm.shell.sysui.ShellSharedConstants;
 
 
 import org.junit.Before;
-import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
@@ -86,9 +85,6 @@ public class BackAnimationControllerTest extends ShellTestCase {
 
     private static final String ANIMATION_ENABLED = "1";
     private final TestShellExecutor mShellExecutor = new TestShellExecutor();
-    @Rule
-    public TestableContext mContext =
-            new TestableContext(InstrumentationRegistry.getInstrumentation().getContext());
 
     private ShellInit mShellInit;
     @Mock
@@ -112,6 +108,9 @@ public class BackAnimationControllerTest extends ShellTestCase {
     @Mock
     private BackAnimationBackground mAnimationBackground;
 
+    @Mock
+    private InputManager mInputManager;
+
     private BackAnimationController mController;
     private TestableContentResolver mContentResolver;
     private TestableLooper mTestableLooper;
@@ -121,6 +120,7 @@ public class BackAnimationControllerTest extends ShellTestCase {
     @Before
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
+        mContext.addMockSystemService(InputManager.class, mInputManager);
         mContext.getApplicationInfo().privateFlags |= ApplicationInfo.PRIVATE_FLAG_PRIVILEGED;
         mContentResolver = new TestableContentResolver(mContext);
         mContentResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider());
@@ -224,7 +224,7 @@ public class BackAnimationControllerTest extends ShellTestCase {
                     .setPrepareRemoteAnimation(true)
                     .setOnBackNavigationDone(new RemoteCallback(result)));
             triggerBackGesture();
-            simulateRemoteAnimationStart(type);
+            simulateRemoteAnimationStart();
             mShellExecutor.flushAll();
             releaseBackGesture();
             simulateRemoteAnimationFinished();
@@ -248,7 +248,7 @@ public class BackAnimationControllerTest extends ShellTestCase {
         // Check that back start and progress is dispatched when first move.
         doMotionEvent(MotionEvent.ACTION_MOVE, 100);
 
-        simulateRemoteAnimationStart(BackNavigationInfo.TYPE_RETURN_TO_HOME);
+        simulateRemoteAnimationStart();
 
         verify(mAnimatorCallback).onBackStarted(any(BackMotionEvent.class));
         verify(mBackAnimationRunner).onAnimationStart(anyInt(), any(), any(), any(), any());
@@ -274,7 +274,7 @@ public class BackAnimationControllerTest extends ShellTestCase {
         // Check that back start and progress is dispatched when first move.
         doMotionEvent(MotionEvent.ACTION_MOVE, 100, 3000);
 
-        simulateRemoteAnimationStart(BackNavigationInfo.TYPE_RETURN_TO_HOME);
+        simulateRemoteAnimationStart();
 
         verify(mAnimatorCallback).onBackStarted(any(BackMotionEvent.class));
         verify(mBackAnimationRunner).onAnimationStart(anyInt(), any(), any(), any(), any());
@@ -327,14 +327,14 @@ public class BackAnimationControllerTest extends ShellTestCase {
     }
 
     @Test
-    public void ignoresGesture_transitionInProgress() throws RemoteException {
+    public void gestureQueued_WhenPreviousTransitionHasNotYetEnded() throws RemoteException {
         registerAnimation(BackNavigationInfo.TYPE_RETURN_TO_HOME);
         createNavigationInfo(BackNavigationInfo.TYPE_RETURN_TO_HOME,
                 /* enableAnimation = */ true,
                 /* isAnimationCallback = */ false);
 
         triggerBackGesture();
-        simulateRemoteAnimationStart(BackNavigationInfo.TYPE_RETURN_TO_HOME);
+        simulateRemoteAnimationStart();
         releaseBackGesture();
 
         // Check that back invocation is dispatched.
@@ -344,24 +344,64 @@ public class BackAnimationControllerTest extends ShellTestCase {
         reset(mAnimatorCallback);
         reset(mBackAnimationRunner);
 
-        // Verify that we prevent animation from restarting if another gestures happens before
-        // the previous transition is finished.
-        doMotionEvent(MotionEvent.ACTION_DOWN, 0);
+        // Verify that we prevent any interaction with the animator callback in case a new gesture
+        // starts while the current back animation has not ended, instead the gesture is queued
+        triggerBackGesture();
         verifyNoMoreInteractions(mAnimatorCallback);
 
-        // Finish back navigation.
+        // Finish previous back navigation.
         simulateRemoteAnimationFinished();
 
-        // Verify that more events from a rejected swipe cannot start animation.
+        // Verify that releasing the gesture causes back key to be injected
+        releaseBackGesture();
+        verify(mInputManager, times(2))
+                .injectInputEvent(any(KeyEvent.class), any(Integer.class));
+
+        // Verify that we start accepting gestures again once transition finishes.
+        doMotionEvent(MotionEvent.ACTION_DOWN, 0);
         doMotionEvent(MotionEvent.ACTION_MOVE, 100);
-        doMotionEvent(MotionEvent.ACTION_UP, 0);
+
+        simulateRemoteAnimationStart();
+        verify(mAnimatorCallback).onBackStarted(any());
+        verify(mBackAnimationRunner).onAnimationStart(anyInt(), any(), any(), any(), any());
+    }
+
+    @Test
+    public void queuedFinishedGesture_RunsAfterPreviousTransitionEnded() throws RemoteException {
+        registerAnimation(BackNavigationInfo.TYPE_RETURN_TO_HOME);
+        createNavigationInfo(BackNavigationInfo.TYPE_RETURN_TO_HOME,
+                /* enableAnimation = */ true,
+                /* isAnimationCallback = */ false);
+
+        triggerBackGesture();
+        simulateRemoteAnimationStart();
+        releaseBackGesture();
+
+        // Check that back invocation is dispatched.
+        verify(mAnimatorCallback).onBackInvoked();
+        verify(mBackAnimationRunner).onAnimationStart(anyInt(), any(), any(), any(), any());
+
+        reset(mAnimatorCallback);
+        reset(mBackAnimationRunner);
+
+        // Verify that we prevent any interaction with the animator callback in case a new gesture
+        // starts while the current back animation has not ended, instead the gesture is queued
+        triggerBackGesture();
+        releaseBackGesture();
         verifyNoMoreInteractions(mAnimatorCallback);
 
+        // Finish previous back navigation.
+        simulateRemoteAnimationFinished();
+
+        // Verify that back key press is injected after previous back navigation has ended
+        verify(mInputManager, times(2))
+                .injectInputEvent(any(KeyEvent.class), any(Integer.class));
+
         // Verify that we start accepting gestures again once transition finishes.
         doMotionEvent(MotionEvent.ACTION_DOWN, 0);
         doMotionEvent(MotionEvent.ACTION_MOVE, 100);
 
-        simulateRemoteAnimationStart(BackNavigationInfo.TYPE_RETURN_TO_HOME);
+        simulateRemoteAnimationStart();
         verify(mAnimatorCallback).onBackStarted(any());
         verify(mBackAnimationRunner).onAnimationStart(anyInt(), any(), any(), any(), any());
     }
@@ -377,7 +417,7 @@ public class BackAnimationControllerTest extends ShellTestCase {
         doNothing().when(mAnimatorCallback).onBackInvoked();
 
         triggerBackGesture();
-        simulateRemoteAnimationStart(BackNavigationInfo.TYPE_RETURN_TO_HOME);
+        simulateRemoteAnimationStart();
         mShellExecutor.flushAll();
 
         releaseBackGesture();
@@ -388,7 +428,7 @@ public class BackAnimationControllerTest extends ShellTestCase {
 
         doMotionEvent(MotionEvent.ACTION_DOWN, 0);
         doMotionEvent(MotionEvent.ACTION_MOVE, 100);
-        simulateRemoteAnimationStart(BackNavigationInfo.TYPE_RETURN_TO_HOME);
+        simulateRemoteAnimationStart();
         verify(mAnimatorCallback).onBackStarted(any());
     }
 
@@ -404,7 +444,7 @@ public class BackAnimationControllerTest extends ShellTestCase {
         // Check that back start and progress is dispatched when first move.
         doMotionEvent(MotionEvent.ACTION_MOVE, 100);
 
-        simulateRemoteAnimationStart(BackNavigationInfo.TYPE_RETURN_TO_HOME);
+        simulateRemoteAnimationStart();
         verify(mAnimatorCallback).onBackStarted(any());
         verify(mBackAnimationRunner).onAnimationStart(anyInt(), any(), any(), any(), any());
 
@@ -443,7 +483,7 @@ public class BackAnimationControllerTest extends ShellTestCase {
                     .setPrepareRemoteAnimation(true)
                     .setOnBackNavigationDone(new RemoteCallback(result)));
             triggerBackGesture();
-            simulateRemoteAnimationStart(type);
+            simulateRemoteAnimationStart();
             mShellExecutor.flushAll();
 
             releaseBackGesture();
@@ -527,7 +567,7 @@ public class BackAnimationControllerTest extends ShellTestCase {
         // Check that back start and progress is dispatched when first move.
         doMotionEvent(MotionEvent.ACTION_MOVE, 100);
 
-        simulateRemoteAnimationStart(type);
+        simulateRemoteAnimationStart();
 
         verify(callback).onBackStarted(any(BackMotionEvent.class));
         verify(animationRunner).startAnimation(any(), any(), any(), any());
@@ -552,7 +592,7 @@ public class BackAnimationControllerTest extends ShellTestCase {
                 /* swipeEdge */ BackEvent.EDGE_LEFT);
     }
 
-    private void simulateRemoteAnimationStart(int type) throws RemoteException {
+    private void simulateRemoteAnimationStart() throws RemoteException {
         RemoteAnimationTarget animationTarget = createAnimationTarget();
         RemoteAnimationTarget[] targets = new RemoteAnimationTarget[]{animationTarget};
         if (mController.mBackAnimationAdapter != null) {
@@ -564,7 +604,7 @@ public class BackAnimationControllerTest extends ShellTestCase {
 
     private void simulateRemoteAnimationFinished() {
         mController.onBackAnimationFinished();
-        mController.finishBackNavigation();
+        mController.finishBackNavigation(/*triggerBack*/ true);
     }
 
     private void registerAnimation(int type) {
@@ -590,5 +630,4 @@ public class BackAnimationControllerTest extends ShellTestCase {
             mTriggerBack = result.getBoolean(KEY_TRIGGER_BACK);
         }
     }
-    ;
 }