Secondary split screen app will be next to the navigation bar (1/2)

Refactored docked position code into PhoneWindowManager to determine
which side the docked app should go based on the position of the nav bar
in landscape (as portrait will only have top). Fixed the split screen
entrance animation for quick step's overview.

Change-Id: I30f1be9d791c23f4cd197f17487609964f78fac0
Fixes: 73250406
Test: play around with splitscreen and minimized mode
Test: atest com.android.server.policy.PhoneWindowManagerTest
diff --git a/core/java/com/android/internal/policy/DividerSnapAlgorithm.java b/core/java/com/android/internal/policy/DividerSnapAlgorithm.java
index 3af3e2a..a3c7a9e 100644
--- a/core/java/com/android/internal/policy/DividerSnapAlgorithm.java
+++ b/core/java/com/android/internal/policy/DividerSnapAlgorithm.java
@@ -344,7 +344,7 @@
             if (dockedSide == DOCKED_LEFT) {
                 position += mInsets.left;
             } else if (dockedSide == DOCKED_RIGHT) {
-                position = mDisplayWidth - position - mInsets.right;
+                position = mDisplayWidth - position - mInsets.right - mDividerSize;
             }
         }
         mTargets.add(new SnapTarget(position, position, SnapTarget.FLAG_NONE));
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java
index 07b980e..ed2f831 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java
@@ -26,6 +26,10 @@
 import android.view.WindowManager;
 import android.view.WindowManagerGlobal;
 
+import static android.view.WindowManagerPolicyConstants.NAV_BAR_RIGHT;
+import static android.view.WindowManagerPolicyConstants.NAV_BAR_BOTTOM;
+import static android.view.WindowManagerPolicyConstants.NAV_BAR_LEFT;
+
 import com.android.systemui.shared.recents.view.AppTransitionAnimationSpecsFuture;
 import com.android.systemui.shared.recents.view.RecentsTransition;
 
@@ -58,6 +62,11 @@
     public static final int TRANSIT_KEYGUARD_OCCLUDE = WindowManager.TRANSIT_KEYGUARD_OCCLUDE;
     public static final int TRANSIT_KEYGUARD_UNOCCLUDE = WindowManager.TRANSIT_KEYGUARD_UNOCCLUDE;
 
+    public static final int NAV_BAR_POS_INVALID = -1;
+    public static final int NAV_BAR_POS_LEFT = NAV_BAR_LEFT;
+    public static final int NAV_BAR_POS_RIGHT = NAV_BAR_RIGHT;
+    public static final int NAV_BAR_POS_BOTTOM = NAV_BAR_BOTTOM;
+
     public static final int ACTIVITY_TYPE_STANDARD = WindowConfiguration.ACTIVITY_TYPE_STANDARD;
 
     public static final int WINDOWING_MODE_UNDEFINED = WindowConfiguration.WINDOWING_MODE_UNDEFINED;
@@ -141,4 +150,20 @@
             Log.w(TAG, "Failed to set recents visibility");
         }
     }
+
+    /**
+     * @return The side of the screen where navigation bar is positioned.
+     * @see #NAV_BAR_POS_RIGHT
+     * @see #NAV_BAR_POS_LEFT
+     * @see #NAV_BAR_POS_BOTTOM
+     * @see #NAV_BAR_POS_INVALID
+     */
+    public int getNavBarPosition() {
+        try {
+            return WindowManagerGlobal.getWindowManagerService().getNavBarPosition();
+        } catch (RemoteException e) {
+            Log.w(TAG, "Failed to get nav bar position");
+        }
+        return NAV_BAR_POS_INVALID;
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
index 1149ad1..79fea9f 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
@@ -21,6 +21,8 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
 import static android.view.PointerIcon.TYPE_HORIZONTAL_DOUBLE_ARROW;
 import static android.view.PointerIcon.TYPE_VERTICAL_DOUBLE_ARROW;
+import static android.view.WindowManager.DOCKED_LEFT;
+import static android.view.WindowManager.DOCKED_RIGHT;
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
@@ -364,14 +366,7 @@
         if (mStableInsets.isEmpty()) {
             SystemServicesProxy.getInstance(mContext).getStableInsets(mStableInsets);
         }
-        int position = (int) (mState.mRatioPositionBeforeMinimized *
-                (isHorizontalDivision() ? mDisplayHeight : mDisplayWidth));
-        mSnapAlgorithm = null;
-        initializeSnapAlgorithm();
-
-        // Set the snap target before minimized but do not save until divider is attached and not
-        // minimized because it does not know its minimized state yet.
-        mSnapTargetBeforeMinimized = mSnapAlgorithm.calculateNonDismissingSnapTarget(position);
+        repositionSnapTargetBeforeMinimized();
     }
 
     public WindowManagerProxy getWindowManagerProxy() {
@@ -878,9 +873,36 @@
     }
 
     public void notifyDockSideChanged(int newDockSide) {
+        int oldDockSide = mDockSide;
         mDockSide = newDockSide;
         mMinimizedShadow.setDockSide(mDockSide);
         requestLayout();
+
+        // Update the snap position to the new docked side with correct insets
+        SystemServicesProxy.getInstance(mContext).getStableInsets(mStableInsets);
+        mMinimizedSnapAlgorithm = null;
+        initializeSnapAlgorithm();
+
+        if (oldDockSide == DOCKED_LEFT && mDockSide == DOCKED_RIGHT
+                || oldDockSide == DOCKED_RIGHT && mDockSide == DOCKED_LEFT) {
+            repositionSnapTargetBeforeMinimized();
+        }
+
+        // Landscape to seascape rotation requires minimized to resize docked app correctly
+        if (mHomeStackResizable && mDockedStackMinimized) {
+            resizeStack(mMinimizedSnapAlgorithm.getMiddleTarget());
+        }
+    }
+
+    private void repositionSnapTargetBeforeMinimized() {
+        int position = (int) (mState.mRatioPositionBeforeMinimized *
+                (isHorizontalDivision() ? mDisplayHeight : mDisplayWidth));
+        mSnapAlgorithm = null;
+        initializeSnapAlgorithm();
+
+        // Set the snap target before minimized but do not save until divider is attached and not
+        // minimized because it does not know its minimized state yet.
+        mSnapTargetBeforeMinimized = mSnapAlgorithm.calculateNonDismissingSnapTarget(position);
     }
 
     private void updateDisplayInfo() {
@@ -962,6 +984,12 @@
         if (mHomeStackResizable && mIsInMinimizeInteraction) {
             calculateBoundsForPosition(mSnapTargetBeforeMinimized.position, mDockSide,
                     mDockedTaskRect);
+
+            // Move a right-docked-app to line up with the divider while dragging it
+            if (mDockSide == DOCKED_RIGHT) {
+                mDockedTaskRect.offset(Math.max(position, mStableInsets.left - mDividerSize)
+                        - mDockedTaskRect.left + mDividerSize, 0);
+            }
             calculateBoundsForPosition(mSnapTargetBeforeMinimized.position,
                     DockedDividerUtils.invertDockSide(mDockSide), mOtherTaskRect);
             mWindowManagerProxy.resizeDockedStack(mDockedRect, mDockedTaskRect, mDockedTaskRect,
@@ -976,6 +1004,12 @@
                 calculateBoundsForPosition(isHorizontalDivision() ? mDisplayHeight : mDisplayWidth,
                         mDockSide, mDockedTaskRect);
             }
+
+            // Move a docked app if from the right in position with the divider up to insets
+            if (mDockSide == DOCKED_RIGHT) {
+                mDockedTaskRect.offset(Math.max(position,
+                        mStableInsets.left) - mDockedTaskRect.left, 0);
+            }
             calculateBoundsForPosition(taskPosition, DockedDividerUtils.invertDockSide(mDockSide),
                     mOtherTaskRect);
             mWindowManagerProxy.resizeDockedStack(mDockedRect, mDockedTaskRect, null,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index 1d64088..77c9f1e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -24,6 +24,8 @@
 import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_ASLEEP;
 import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_AWAKE;
 import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_WAKING;
+import static com.android.systemui.shared.system.WindowManagerWrapper.NAV_BAR_POS_LEFT;
+import static com.android.systemui.shared.system.WindowManagerWrapper.NAV_BAR_POS_INVALID;
 import static com.android.systemui.statusbar.NotificationLockscreenUserManager
         .NOTIFICATION_UNLOCKED_BY_WORK_CHALLENGE_ACTION;
 import static com.android.systemui.statusbar.NotificationLockscreenUserManager.PERMISSION_SELF;
@@ -178,6 +180,7 @@
 import com.android.systemui.recents.events.activity.AppTransitionFinishedEvent;
 import com.android.systemui.recents.events.activity.UndockingTaskEvent;
 import com.android.systemui.recents.misc.SystemServicesProxy;
+import com.android.systemui.shared.system.WindowManagerWrapper;
 import com.android.systemui.stackdivider.Divider;
 import com.android.systemui.stackdivider.WindowManagerProxy;
 import com.android.systemui.statusbar.ActivatableNotificationView;
@@ -207,14 +210,12 @@
 import com.android.systemui.statusbar.NotificationViewHierarchyManager;
 import com.android.systemui.statusbar.RemoteInputController;
 import com.android.systemui.statusbar.ScrimView;
-import com.android.systemui.statusbar.SignalClusterView;
 import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.VibratorHelper;
 import com.android.systemui.statusbar.notification.AboveShelfObserver;
 import com.android.systemui.statusbar.notification.ActivityLaunchAnimator;
 import com.android.systemui.statusbar.notification.VisualStabilityManager;
 import com.android.systemui.statusbar.phone.UnlockMethodCache.OnUnlockMethodChangedListener;
-import com.android.systemui.statusbar.phone.KeyguardDismissUtil;
 import com.android.systemui.statusbar.policy.BatteryController;
 import com.android.systemui.statusbar.policy.BatteryController.BatteryStateChangeCallback;
 import com.android.systemui.statusbar.policy.BrightnessMirrorController;
@@ -1339,8 +1340,15 @@
         }
         int dockSide = WindowManagerProxy.getInstance().getDockSide();
         if (dockSide == WindowManager.DOCKED_INVALID) {
-            return mRecents.splitPrimaryTask(NavigationBarGestureHelper.DRAG_MODE_NONE,
-                    ActivityManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT, null, metricsDockAction);
+            final int navbarPos = WindowManagerWrapper.getInstance().getNavBarPosition();
+            if (navbarPos == NAV_BAR_POS_INVALID) {
+                return false;
+            }
+            int createMode = navbarPos == NAV_BAR_POS_LEFT
+                    ? ActivityManager.SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT
+                    : ActivityManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
+            return mRecents.splitPrimaryTask(NavigationBarGestureHelper.DRAG_MODE_NONE, createMode,
+                    null, metricsDockAction);
         } else {
             Divider divider = getComponent(Divider.class);
             if (divider != null && divider.isMinimized() && !divider.isHomeStackResizable()) {
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 1d5b1a3..18f3434 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -234,6 +234,7 @@
 import android.util.proto.ProtoOutputStream;
 import android.view.Display;
 import android.view.DisplayCutout;
+import android.view.DisplayInfo;
 import android.view.Gravity;
 import android.view.HapticFeedbackConstants;
 import android.view.IApplicationToken;
@@ -7191,14 +7192,35 @@
     }
 
     @Override
-    public boolean isDockSideAllowed(int dockSide) {
+    public boolean isDockSideAllowed(int dockSide, int originalDockSide, int displayWidth,
+            int displayHeight, int displayRotation) {
+        final int barPosition = navigationBarPosition(displayWidth, displayHeight, displayRotation);
+        return isDockSideAllowed(dockSide, originalDockSide, barPosition, mNavigationBarCanMove);
+    }
 
-        // We do not allow all dock sides at which the navigation bar touches the docked stack.
-        if (!mNavigationBarCanMove) {
-            return dockSide == DOCKED_TOP || dockSide == DOCKED_LEFT || dockSide == DOCKED_RIGHT;
-        } else {
-            return dockSide == DOCKED_TOP || dockSide == DOCKED_LEFT;
+    @VisibleForTesting
+    static boolean isDockSideAllowed(int dockSide, int originalDockSide,
+            int navBarPosition, boolean navigationBarCanMove) {
+        if (dockSide == DOCKED_TOP) {
+            return true;
         }
+
+        if (navigationBarCanMove) {
+            // Only allow the dockside opposite to the nav bar position in landscape
+            return dockSide == DOCKED_LEFT && navBarPosition == NAV_BAR_RIGHT
+                    || dockSide == DOCKED_RIGHT && navBarPosition == NAV_BAR_LEFT;
+        }
+
+        // Side is the same as original side
+        if (dockSide == originalDockSide) {
+            return true;
+        }
+
+        // Only if original docked side was top in portrait will allow left for landscape
+        if (dockSide == DOCKED_LEFT && originalDockSide == DOCKED_TOP) {
+            return true;
+        }
+        return false;
     }
 
     void sendCloseSystemWindows() {
diff --git a/services/core/java/com/android/server/policy/WindowManagerPolicy.java b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
index ccbf502..8690a83 100644
--- a/services/core/java/com/android/server/policy/WindowManagerPolicy.java
+++ b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
@@ -1706,11 +1706,19 @@
             DisplayCutout displayCutout, Rect outInsets);
 
     /**
+     * @param displayRotation the current display rotation
+     * @param displayWidth the current display width
+     * @param displayHeight the current display height
+     * @param dockSide the dockside asking if allowed
+     * @param originalDockSide the side that was original docked to in split screen
      * @return True if a specified {@param dockSide} is allowed on the current device, or false
      *         otherwise. It is guaranteed that at least one dock side for a particular orientation
      *         is allowed, so for example, if DOCKED_RIGHT is not allowed, DOCKED_LEFT is allowed.
+     *         If navigation bar is movable then the docked side would bias towards the
+     *         {@param originalDockSide}.
      */
-    public boolean isDockSideAllowed(int dockSide);
+    public boolean isDockSideAllowed(int dockSide, int originalDockSide, int displayWidth,
+            int displayHeight, int displayRotation);
 
     /**
      * Called when the configuration has changed, and it's safe to load new values from resources.
diff --git a/services/core/java/com/android/server/wm/DockedStackDividerController.java b/services/core/java/com/android/server/wm/DockedStackDividerController.java
index 2cd2ef1..c8baced 100644
--- a/services/core/java/com/android/server/wm/DockedStackDividerController.java
+++ b/services/core/java/com/android/server/wm/DockedStackDividerController.java
@@ -170,7 +170,7 @@
             final int orientation = mTmpRect2.width() <= mTmpRect2.height()
                     ? ORIENTATION_PORTRAIT
                     : ORIENTATION_LANDSCAPE;
-            final int dockSide = TaskStack.getDockSideUnchecked(mTmpRect, mTmpRect2, orientation);
+            final int dockSide = getDockSide(mTmpRect, mTmpRect2, orientation);
             final int position = DockedDividerUtils.calculatePositionForBounds(mTmpRect, dockSide,
                     getContentWidth());
 
@@ -191,6 +191,39 @@
         return (int) (minWidth / mDisplayContent.getDisplayMetrics().density);
     }
 
+    /**
+     * Get the current docked side. Determined by its location of {@param bounds} within
+     * {@param displayRect} but if both are the same, it will try to dock to each side and determine
+     * if allowed in its respected {@param orientation}.
+     *
+     * @param bounds bounds of the docked task to get which side is docked
+     * @param displayRect bounds of the display that contains the docked task
+     * @param orientation the origination of device
+     * @return current docked side
+     */
+    int getDockSide(Rect bounds, Rect displayRect, int orientation) {
+        if (orientation == Configuration.ORIENTATION_PORTRAIT) {
+            // Portrait mode, docked either at the top or the bottom.
+            final int diff = (displayRect.bottom - bounds.bottom) - (bounds.top - displayRect.top);
+            if (diff > 0) {
+                return DOCKED_TOP;
+            } else if (diff < 0) {
+                return DOCKED_BOTTOM;
+            }
+            return canPrimaryStackDockTo(DOCKED_TOP) ? DOCKED_TOP : DOCKED_BOTTOM;
+        } else if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
+            // Landscape mode, docked either on the left or on the right.
+            final int diff = (displayRect.right - bounds.right) - (bounds.left - displayRect.left);
+            if (diff > 0) {
+                return DOCKED_LEFT;
+            } else if (diff < 0) {
+                return DOCKED_RIGHT;
+            }
+            return canPrimaryStackDockTo(DOCKED_LEFT) ? DOCKED_LEFT : DOCKED_RIGHT;
+        }
+        return DOCKED_INVALID;
+    }
+
     void getHomeStackBoundsInDockedMode(Rect outBounds) {
         final DisplayInfo di = mDisplayContent.getDisplayInfo();
         mService.mPolicy.getStableInsetsLw(di.rotation, di.logicalWidth, di.logicalHeight,
@@ -203,10 +236,20 @@
             outBounds.set(0, mTaskHeightInMinimizedMode + dividerSize + mTmpRect.top,
                     di.logicalWidth, di.logicalHeight);
         } else {
-            // In landscape append the left position with the statusbar height to match the
+            // In landscape also inset the left/right side with the statusbar height to match the
             // minimized size height in portrait mode.
-            outBounds.set(mTaskHeightInMinimizedMode + dividerSize + mTmpRect.left + mTmpRect.top,
-                    0, di.logicalWidth, di.logicalHeight);
+            final TaskStack stack = mDisplayContent.getSplitScreenPrimaryStackIgnoringVisibility();
+            final int primaryTaskWidth = mTaskHeightInMinimizedMode + dividerSize + mTmpRect.top;
+            int left = mTmpRect.left;
+            int right = di.logicalWidth - mTmpRect.right;
+            if (stack != null) {
+                if (stack.getDockSide() == DOCKED_LEFT) {
+                    left += primaryTaskWidth;
+                } else if (stack.getDockSide() == DOCKED_RIGHT) {
+                    right -= primaryTaskWidth;
+                }
+            }
+            outBounds.set(left, 0, right, di.logicalHeight);
         }
     }
 
@@ -420,21 +463,9 @@
      * @return true if the side provided is valid
      */
     boolean canPrimaryStackDockTo(int dockSide) {
-        if (mService.mPolicy.isDockSideAllowed(dockSide)) {
-            // Side is the same as original side
-            if (dockSide == mOriginalDockedSide) {
-                return true;
-            }
-            // Special rule that the top in portrait is always valid
-            if (dockSide == DOCKED_TOP) {
-                return true;
-            }
-            // Only if original docked side was top in portrait will allow left side for landscape
-            if (dockSide == DOCKED_LEFT && mOriginalDockedSide == DOCKED_TOP) {
-                return true;
-            }
-        }
-        return false;
+        final DisplayInfo di = mDisplayContent.getDisplayInfo();
+        return mService.mPolicy.isDockSideAllowed(dockSide, mOriginalDockedSide, di.logicalWidth,
+                di.logicalHeight, di.rotation);
     }
 
     void notifyDockedStackExistsChanged(boolean exists) {
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index 018765d..891ee2e 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -1462,27 +1462,7 @@
         }
         dc.getBounds(mTmpRect);
         final int orientation = dc.getConfiguration().orientation;
-        return getDockSideUnchecked(bounds, mTmpRect, orientation);
-    }
-
-    static int getDockSideUnchecked(Rect bounds, Rect displayRect, int orientation) {
-        if (orientation == Configuration.ORIENTATION_PORTRAIT) {
-            // Portrait mode, docked either at the top or the bottom.
-            if (bounds.top - displayRect.top <= displayRect.bottom - bounds.bottom) {
-                return DOCKED_TOP;
-            } else {
-                return DOCKED_BOTTOM;
-            }
-        } else if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
-            // Landscape mode, docked either on the left or on the right.
-            if (bounds.left - displayRect.left <= displayRect.right - bounds.right) {
-                return DOCKED_LEFT;
-            } else {
-                return DOCKED_RIGHT;
-            }
-        } else {
-            return DOCKED_INVALID;
-        }
+        return dc.getDockedDividerController().getDockSide(bounds, mTmpRect, orientation);
     }
 
     boolean hasTaskForUser(int userId) {
diff --git a/services/tests/servicestests/src/com/android/server/policy/PhoneWindowManagerTest.java b/services/tests/servicestests/src/com/android/server/policy/PhoneWindowManagerTest.java
index 64637f4..30665b5 100644
--- a/services/tests/servicestests/src/com/android/server/policy/PhoneWindowManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/policy/PhoneWindowManagerTest.java
@@ -28,12 +28,19 @@
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
 import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
 import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
+import static android.view.WindowManager.DOCKED_BOTTOM;
+import static android.view.WindowManager.DOCKED_LEFT;
+import static android.view.WindowManager.DOCKED_RIGHT;
+import static android.view.WindowManager.DOCKED_TOP;
 
+import static com.android.server.policy.WindowManagerPolicy.NAV_BAR_LEFT;
 import static com.android.server.policy.WindowManagerPolicy.NAV_BAR_BOTTOM;
 import static com.android.server.policy.WindowManagerPolicy.NAV_BAR_RIGHT;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
 
 import android.graphics.PixelFormat;
 import android.platform.test.annotations.Presubmit;
@@ -194,4 +201,53 @@
                 PhoneWindowManager.updateLightNavigationBarLw(0, opaqueDarkNavBar,
                         opaqueDarkNavBar, imeDrawLightNavBar, imeDrawLightNavBar));
     }
+
+    @Test
+    public void testIsDockSideAllowedDockTop() throws Exception {
+        // Docked top is always allowed
+        assertTrue(PhoneWindowManager.isDockSideAllowed(DOCKED_TOP, DOCKED_LEFT, NAV_BAR_BOTTOM,
+                true /* navigationBarCanMove */));
+        assertTrue(PhoneWindowManager.isDockSideAllowed(DOCKED_TOP, DOCKED_LEFT, NAV_BAR_BOTTOM,
+                false /* navigationBarCanMove */));
+    }
+
+    @Test
+    public void testIsDockSideAllowedDockBottom() throws Exception {
+        // Cannot dock bottom
+        assertFalse(PhoneWindowManager.isDockSideAllowed(DOCKED_BOTTOM, DOCKED_LEFT, NAV_BAR_BOTTOM,
+                true /* navigationBarCanMove */));
+    }
+
+    @Test
+    public void testIsDockSideAllowedNavigationBarMovable() throws Exception {
+        assertFalse(PhoneWindowManager.isDockSideAllowed(DOCKED_LEFT, DOCKED_LEFT, NAV_BAR_BOTTOM,
+                true /* navigationBarCanMove */));
+        assertFalse(PhoneWindowManager.isDockSideAllowed(DOCKED_LEFT, DOCKED_LEFT, NAV_BAR_LEFT,
+                true /* navigationBarCanMove */));
+        assertTrue(PhoneWindowManager.isDockSideAllowed(DOCKED_LEFT, DOCKED_LEFT, NAV_BAR_RIGHT,
+                true /* navigationBarCanMove */));
+        assertFalse(PhoneWindowManager.isDockSideAllowed(DOCKED_RIGHT, DOCKED_LEFT, NAV_BAR_BOTTOM,
+                true /* navigationBarCanMove */));
+        assertFalse(PhoneWindowManager.isDockSideAllowed(DOCKED_RIGHT, DOCKED_LEFT, NAV_BAR_RIGHT,
+                true /* navigationBarCanMove */));
+        assertTrue(PhoneWindowManager.isDockSideAllowed(DOCKED_RIGHT, DOCKED_LEFT, NAV_BAR_LEFT,
+                true /* navigationBarCanMove */));
+    }
+
+    @Test
+    public void testIsDockSideAllowedNavigationBarNotMovable() throws Exception {
+        // Navigation bar is not movable such as tablets
+        assertTrue(PhoneWindowManager.isDockSideAllowed(DOCKED_LEFT, DOCKED_LEFT, NAV_BAR_BOTTOM,
+                false /* navigationBarCanMove */));
+        assertTrue(PhoneWindowManager.isDockSideAllowed(DOCKED_LEFT, DOCKED_TOP, NAV_BAR_BOTTOM,
+                false /* navigationBarCanMove */));
+        assertFalse(PhoneWindowManager.isDockSideAllowed(DOCKED_LEFT, DOCKED_RIGHT, NAV_BAR_BOTTOM,
+                false /* navigationBarCanMove */));
+        assertFalse(PhoneWindowManager.isDockSideAllowed(DOCKED_RIGHT, DOCKED_LEFT, NAV_BAR_BOTTOM,
+                false /* navigationBarCanMove */));
+        assertFalse(PhoneWindowManager.isDockSideAllowed(DOCKED_RIGHT, DOCKED_TOP, NAV_BAR_BOTTOM,
+                false /* navigationBarCanMove */));
+        assertTrue(PhoneWindowManager.isDockSideAllowed(DOCKED_RIGHT, DOCKED_RIGHT, NAV_BAR_BOTTOM,
+                false /* navigationBarCanMove */));
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java b/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java
index 1c2d538..013c672 100644
--- a/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java
+++ b/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java
@@ -552,7 +552,8 @@
     }
 
     @Override
-    public boolean isDockSideAllowed(int dockSide) {
+    public boolean isDockSideAllowed(int dockSide, int originalDockSide, int displayWidth,
+            int displayHeight, int displayRotation) {
         return false;
     }