Merge changes Id8d6f639,Iedc9e21b

* changes:
  Support creating docked stack at top/left/bottom/right half of screen
  Display dim layer when task is dragged to screen edge
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index eeae20f..9ef51c8 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -452,6 +452,22 @@
      */
     public static final int FIRST_DYNAMIC_STACK_ID = LAST_STATIC_STACK_ID + 1;
 
+    /**
+     * Input parameter to {@link android.app.IActivityManager#moveTaskToDockedStack} which
+     * specifies the position of the created docked stack at the top half of the screen if
+     * in portrait mode or at the left half of the screen if in landscape mode.
+     * @hide
+     */
+    public static final int DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT = 0;
+
+    /**
+     * Input parameter to {@link android.app.IActivityManager#moveTaskToDockedStack} which
+     * specifies the position of the created docked stack at the bottom half of the screen if
+     * in portrait mode or at the right half of the screen if in landscape mode.
+     * @hide
+     */
+    public static final int DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT = 1;
+
     /** @hide */
     public int getFrontActivityScreenCompatMode() {
         try {
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index d1c73bc..d79716c 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -743,6 +743,16 @@
             return true;
         }
 
+        case MOVE_TASK_TO_DOCKED_STACK_TRANSACTION: {
+            data.enforceInterface(IActivityManager.descriptor);
+            int taskId = data.readInt();
+            int createMode = data.readInt();
+            boolean toTop = data.readInt() != 0;
+            moveTaskToDockedStack(taskId, createMode, toTop);
+            reply.writeNoException();
+            return true;
+        }
+
         case RESIZE_STACK_TRANSACTION: {
             data.enforceInterface(IActivityManager.descriptor);
             int stackId = data.readInt();
@@ -3510,6 +3520,21 @@
         reply.recycle();
     }
     @Override
+    public void moveTaskToDockedStack(int taskId, int createMode, boolean toTop)
+            throws RemoteException
+    {
+        Parcel data = Parcel.obtain();
+        Parcel reply = Parcel.obtain();
+        data.writeInterfaceToken(IActivityManager.descriptor);
+        data.writeInt(taskId);
+        data.writeInt(createMode);
+        data.writeInt(toTop ? 1 : 0);
+        mRemote.transact(MOVE_TASK_TO_DOCKED_STACK_TRANSACTION, data, reply, 0);
+        reply.readException();
+        data.recycle();
+        reply.recycle();
+    }
+    @Override
     public void resizeStack(int stackId, Rect r) throws RemoteException
     {
         Parcel data = Parcel.obtain();
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index 66fa796..3b30d71 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -140,6 +140,8 @@
     public boolean moveActivityTaskToBack(IBinder token, boolean nonRoot) throws RemoteException;
     public void moveTaskBackwards(int task) throws RemoteException;
     public void moveTaskToStack(int taskId, int stackId, boolean toTop) throws RemoteException;
+    public void moveTaskToDockedStack(int taskId, int createMode, boolean toTop)
+            throws RemoteException;
     public void resizeStack(int stackId, Rect bounds) throws RemoteException;
     public void positionTaskInStack(int taskId, int stackId, int position) throws RemoteException;
     public List<StackInfo> getAllStackInfos() throws RemoteException;
@@ -888,4 +890,5 @@
     int GET_ACTIVITY_STACK_ID_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 343;
     int MOVE_ACTIVITY_TO_STACK_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 344;
     int REPORT_SIZE_CONFIGURATIONS = IBinder.FIRST_CALL_TRANSACTION + 345;
+    int MOVE_TASK_TO_DOCKED_STACK_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 346;
 }
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 14376d1..dc6f3d6 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -19,7 +19,6 @@
 import static android.Manifest.permission.INTERACT_ACROSS_USERS;
 import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
 import static android.Manifest.permission.START_TASKS_FROM_RECENTS;
-import static android.app.ActivityManager.FREEFORM_WORKSPACE_STACK_ID;
 import static android.app.ActivityManager.DOCKED_STACK_ID;
 import static android.app.ActivityManager.HOME_STACK_ID;
 import static android.app.ActivityManager.INVALID_STACK_ID;
@@ -215,7 +214,6 @@
 import android.os.StrictMode;
 import android.os.SystemClock;
 import android.os.SystemProperties;
-import android.os.Trace;
 import android.os.UpdateLock;
 import android.os.UserHandle;
 import android.os.UserManager;
@@ -9053,6 +9051,35 @@
         }
     }
 
+    /**
+     * Moves the input task to the docked stack.
+     *
+     * @param taskId Id of task to move.
+     * @param createMode The mode the docked stack should be created in if it doesn't exist
+     *                   already. See
+     *                   {@link android.app.ActivityManager#DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT}
+     *                   and
+     *                   {@link android.app.ActivityManager#DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT}
+     * @param toTop If the task and stack should be moved to the top.
+     */
+    @Override
+    public void moveTaskToDockedStack(int taskId, int createMode, boolean toTop) {
+        enforceCallingPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS,
+                "moveTaskToDockedStack()");
+        synchronized (this) {
+            long ident = Binder.clearCallingIdentity();
+            try {
+                if (DEBUG_STACK) Slog.d(TAG_STACK, "moveTaskToDockedStack: moving task=" + taskId
+                        + " to createMode=" + createMode + " toTop=" + toTop);
+                mWindowManager.setDockedStackCreateMode(createMode);
+                mStackSupervisor.moveTaskToStackLocked(
+                        taskId, DOCKED_STACK_ID, toTop, !FORCE_FOCUS);
+            } finally {
+                Binder.restoreCallingIdentity(ident);
+            }
+        }
+    }
+
     @Override
     public void resizeStack(int stackId, Rect bounds) {
         enforceCallingPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS,
diff --git a/services/core/java/com/android/server/wm/TaskPositioner.java b/services/core/java/com/android/server/wm/TaskPositioner.java
index 71b83a5..876e9aa 100644
--- a/services/core/java/com/android/server/wm/TaskPositioner.java
+++ b/services/core/java/com/android/server/wm/TaskPositioner.java
@@ -16,13 +16,12 @@
 
 package com.android.server.wm;
 
-import com.android.server.input.InputApplicationHandle;
-import com.android.server.input.InputWindowHandle;
-import com.android.server.wm.WindowManagerService.H;
+import static android.app.ActivityManager.DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT;
+import static android.app.ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT;
+import static android.app.ActivityManager.FREEFORM_WORKSPACE_STACK_ID;
+import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
 import static com.android.server.wm.WindowManagerService.DEBUG_TASK_POSITIONING;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
+import static com.android.server.wm.WindowManagerService.SHOW_TRANSACTIONS;
 
 import android.annotation.IntDef;
 import android.graphics.Point;
@@ -34,16 +33,29 @@
 import android.util.Slog;
 import android.util.TypedValue;
 import android.view.Display;
+import android.view.DisplayInfo;
 import android.view.InputChannel;
 import android.view.InputDevice;
 import android.view.InputEvent;
 import android.view.InputEventReceiver;
 import android.view.MotionEvent;
+import android.view.SurfaceControl;
 import android.view.WindowManager;
 
-class TaskPositioner {
+import com.android.server.input.InputApplicationHandle;
+import com.android.server.input.InputWindowHandle;
+import com.android.server.wm.WindowManagerService.H;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+class TaskPositioner implements DimLayer.DimLayerUser {
     private static final String TAG = "TaskPositioner";
 
+    // The margin the pointer position has to be within the side of the screen to be
+    // considered at the side of the screen.
+    private static final int SIDE_MARGIN_DIP = 5;
+
     @IntDef(flag = true,
             value = {
                     CTRL_NONE,
@@ -65,8 +77,14 @@
     private WindowPositionerEventReceiver mInputEventReceiver;
     private Display mDisplay;
     private final DisplayMetrics mDisplayMetrics = new DisplayMetrics();
+    private DimLayer mDimLayer;
+    @CtrlType
+    private int mCurrentDimSide;
+    private Rect mTmpRect = new Rect();
+    private int mSideMargin;
 
     private int mTaskId;
+    private TaskStack mStack;
     private final Rect mWindowOriginalBounds = new Rect();
     private final Rect mWindowDragBounds = new Rect();
     private float mStartDragX;
@@ -136,6 +154,13 @@
                     // Post back to WM to handle clean-ups. We still need the input
                     // event handler for the last finishInputEvent()!
                     mService.mH.sendEmptyMessage(H.FINISH_TASK_POSITIONING);
+                    if (mCurrentDimSide != CTRL_NONE) {
+                        final int createMode = mCurrentDimSide == CTRL_LEFT
+                                ? DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT
+                                : DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT;
+                        mService.mActivityManager.moveTaskToDockedStack(
+                                mTaskId, createMode, true /*toTop*/);
+                    }
                 }
                 handled = true;
             } catch (Exception e) {
@@ -213,6 +238,9 @@
             Slog.d(TAG, "Pausing rotation during re-position");
         }
         mService.pauseRotationLocked();
+
+        mDimLayer = new DimLayer(mService, this, mDisplay.getDisplayId());
+        mSideMargin = (int)dipToPx(SIDE_MARGIN_DIP);
     }
 
     void unregister() {
@@ -238,6 +266,12 @@
         mDragApplicationHandle = null;
         mDisplay = null;
 
+        if (mDimLayer != null) {
+            mDimLayer.destroySurface();
+            mDimLayer = null;
+        }
+        mCurrentDimSide = CTRL_NONE;
+
         // Resume rotations after a drag.
         if (WindowManagerService.DEBUG_ORIENTATION) {
             Slog.d(TAG, "Resuming rotation after re-position");
@@ -246,8 +280,8 @@
     }
 
     void startDragLocked(WindowState win, boolean resize, float startX, float startY) {
-        if (DEBUG_TASK_POSITIONING) {Slog.d(TAG,
-                "startDragLocked: win=" + win + ", resize=" + resize
+        if (DEBUG_TASK_POSITIONING) {
+            Slog.d(TAG, "startDragLocked: win=" + win + ", resize=" + resize
                 + ", {" + startX + ", " + startY + "}");
         }
         mCtrlType = CTRL_NONE;
@@ -269,6 +303,7 @@
 
         final Task task = win.getTask();
         mTaskId = task.mTaskId;
+        mStack = task.mStack;
         mStartDragX = startX;
         mStartDragY = startY;
 
@@ -308,11 +343,77 @@
         } else {
             // This is a moving operation.
             mWindowDragBounds.set(mWindowOriginalBounds);
-            mWindowDragBounds.offset(Math.round(x - mStartDragX),
-                    Math.round(y - mStartDragY));
+            mWindowDragBounds.offset(Math.round(x - mStartDragX), Math.round(y - mStartDragY));
+            updateDimLayerVisibility((int) x);
         }
     }
 
+    private void updateDimLayerVisibility(int x) {
+        @CtrlType
+        int dimSide = getDimSide(x);
+        if (dimSide == mCurrentDimSide) {
+            return;
+        }
+
+        mCurrentDimSide = dimSide;
+
+        if (SHOW_TRANSACTIONS) Slog.i(TAG, ">>> OPEN TRANSACTION updateDimLayerVisibility");
+        SurfaceControl.openTransaction();
+        if (mCurrentDimSide == CTRL_NONE) {
+            mDimLayer.hide();
+        } else {
+            showDimLayer();
+        }
+        SurfaceControl.closeTransaction();
+    }
+
+    /**
+     * Returns the side of the screen the dim layer should be shown.
+     * @param x horizontal coordinate used to determine if the dim layer should be shown
+     * @return Returns {@link #CTRL_LEFT} if the dim layer should be shown on the left half of the
+     * screen, {@link #CTRL_RIGHT} if on the right side, or {@link #CTRL_NONE} if the dim layer
+     * shouldn't be shown.
+     */
+    private int getDimSide(int x) {
+        if (mStack.mStackId != FREEFORM_WORKSPACE_STACK_ID
+                || !mStack.isFullscreen()
+                || mService.mCurConfiguration.orientation != ORIENTATION_LANDSCAPE) {
+            return CTRL_NONE;
+        }
+
+        mStack.getBounds(mTmpRect);
+        if (x - mSideMargin <= mTmpRect.left) {
+            return CTRL_LEFT;
+        }
+        if (x + mSideMargin >= mTmpRect.right) {
+            return CTRL_RIGHT;
+        }
+
+        return CTRL_NONE;
+    }
+
+    private void showDimLayer() {
+        mStack.getBounds(mTmpRect);
+        if (mCurrentDimSide == CTRL_LEFT) {
+            mTmpRect.right = mTmpRect.centerX();
+        } else if (mCurrentDimSide == CTRL_RIGHT) {
+            mTmpRect.left = mTmpRect.centerX();
+        }
+
+        mDimLayer.setBounds(mTmpRect);
+        mDimLayer.show(getDragLayerLocked(), 0.5f, 0);
+    }
+
+    @Override /** {@link DimLayer.DimLayerUser} */
+    public boolean isFullscreen() {
+        return false;
+    }
+
+    @Override /** {@link DimLayer.DimLayerUser} */
+    public DisplayInfo getDisplayInfo() {
+        return mStack.getDisplayInfo();
+    }
+
     private int getDragLayerLocked() {
         return mService.mPolicy.windowTypeToLayerLw(WindowManager.LayoutParams.TYPE_DRAG)
                 * WindowManagerService.TYPE_LAYER_MULTIPLIER
@@ -322,4 +423,4 @@
     private float dipToPx(float dip) {
         return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dip, mDisplayMetrics);
     }
-}
\ No newline at end of file
+}
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index ae3bb9b..1e6fab6 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -353,11 +353,13 @@
     private static void getInitialDockedStackBounds(
             Rect displayRect, Rect outBounds, int stackId) {
         // Docked stack start off occupying half the screen space.
-        // TODO(multi-window): Need to support the selecting which half of the screen the
-        // docked stack uses for snapping windows to the edge of the screen.
         final boolean splitHorizontally = displayRect.width() > displayRect.height();
+        final boolean topOrLeftCreateMode =
+                WindowManagerService.sDockedStackCreateMode == DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT;
+        final boolean placeTopOrLeft = (stackId == DOCKED_STACK_ID && topOrLeftCreateMode)
+                || (stackId != DOCKED_STACK_ID && !topOrLeftCreateMode);
         outBounds.set(displayRect);
-        if (stackId == DOCKED_STACK_ID) {
+        if (placeTopOrLeft) {
             if (splitHorizontally) {
                 outBounds.right = displayRect.centerX();
             } else {
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 69440b8..8719ddb 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -16,6 +16,34 @@
 
 package com.android.server.wm;
 
+import static android.app.ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT;
+import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW;
+import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW;
+import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
+import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
+import static android.view.WindowManager.LayoutParams.FLAG_SECURE;
+import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
+import static android.view.WindowManager.LayoutParams.LAST_APPLICATION_WINDOW;
+import static android.view.WindowManager.LayoutParams.LAST_SUB_WINDOW;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_COMPATIBLE_WINDOW;
+import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
+import static android.view.WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST;
+import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
+import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
+import static android.view.WindowManager.LayoutParams.TYPE_BOOT_PROGRESS;
+import static android.view.WindowManager.LayoutParams.TYPE_DREAM;
+import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
+import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG;
+import static android.view.WindowManager.LayoutParams.TYPE_PRIVATE_PRESENTATION;
+import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
+import static android.view.WindowManager.LayoutParams.TYPE_VOICE_INTERACTION;
+import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
+import static android.view.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
+
+import static com.android.server.wm.WindowState.BOUNDS_FOR_TOUCH;
+
 import android.Manifest;
 import android.animation.ValueAnimator;
 import android.app.ActivityManagerNative;
@@ -110,9 +138,7 @@
 import android.view.WindowManagerPolicy.PointerEventListener;
 import android.view.animation.Animation;
 
-import static com.android.server.wm.WindowState.BOUNDS_FOR_TOUCH;
 import com.android.internal.app.IAssistScreenshotReceiver;
-import com.android.internal.app.IBatteryStats;
 import com.android.internal.util.FastPrintWriter;
 import com.android.internal.view.IInputContext;
 import com.android.internal.view.IInputMethodClient;
@@ -149,31 +175,6 @@
 import java.util.Iterator;
 import java.util.List;
 
-import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW;
-import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW;
-import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
-import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
-import static android.view.WindowManager.LayoutParams.FLAG_SECURE;
-import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
-import static android.view.WindowManager.LayoutParams.LAST_APPLICATION_WINDOW;
-import static android.view.WindowManager.LayoutParams.LAST_SUB_WINDOW;
-import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_COMPATIBLE_WINDOW;
-import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
-import static android.view.WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST;
-import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY;
-import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
-import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
-import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
-import static android.view.WindowManager.LayoutParams.TYPE_BOOT_PROGRESS;
-import static android.view.WindowManager.LayoutParams.TYPE_DREAM;
-import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
-import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG;
-import static android.view.WindowManager.LayoutParams.TYPE_PRIVATE_PRESENTATION;
-import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
-import static android.view.WindowManager.LayoutParams.TYPE_VOICE_INTERACTION;
-import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
-import static android.view.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
-
 /** {@hide} */
 public class WindowManagerService extends IWindowManager.Stub
         implements Watchdog.Monitor, WindowManagerPolicy.WindowManagerFuncs {
@@ -458,6 +459,8 @@
 
     private boolean mKeyguardWaitingForActivityDrawn;
 
+    static int sDockedStackCreateMode = DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT;
+
     class RotationWatcher {
         IRotationWatcher watcher;
         IBinder.DeathRecipient deathRecipient;
@@ -4448,6 +4451,12 @@
         }
     }
 
+    public void setDockedStackCreateMode(int mode) {
+        synchronized (mWindowMap) {
+            sDockedStackCreateMode = mode;
+        }
+    }
+
     /**
      * Create a new TaskStack and place it on a DisplayContent.
      * @param stackId The unique identifier of the new stack.