Merge "Fix freeform window cannot move up" into rvc-dev
diff --git a/services/core/java/com/android/server/wm/TaskPositioner.java b/services/core/java/com/android/server/wm/TaskPositioner.java
index b5892b9..f046e8a 100644
--- a/services/core/java/com/android/server/wm/TaskPositioner.java
+++ b/services/core/java/com/android/server/wm/TaskPositioner.java
@@ -35,7 +35,6 @@
 import static com.android.server.wm.WindowState.MINIMUM_VISIBLE_WIDTH_IN_DP;
 
 import android.annotation.NonNull;
-import android.app.IActivityTaskManager;
 import android.graphics.Point;
 import android.graphics.Rect;
 import android.os.Binder;
@@ -48,7 +47,6 @@
 import android.util.Slog;
 import android.view.BatchedInputEventReceiver;
 import android.view.Choreographer;
-import android.view.Display;
 import android.view.InputApplicationHandle;
 import android.view.InputChannel;
 import android.view.InputDevice;
@@ -75,10 +73,8 @@
     public static final int RESIZING_HINT_DURATION_MS = 0;
 
     private final WindowManagerService mService;
-    private final IActivityTaskManager mActivityManager;
     private WindowPositionerEventReceiver mInputEventReceiver;
     private DisplayContent mDisplayContent;
-    private final DisplayMetrics mDisplayMetrics = new DisplayMetrics();
     private Rect mTmpRect = new Rect();
     private int mMinVisibleWidth;
     private int mMinVisibleHeight;
@@ -151,11 +147,8 @@
                         if (!mTmpRect.equals(mWindowDragBounds)) {
                             Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER,
                                     "wm.TaskPositioner.resizeTask");
-                            try {
-                                mActivityManager.resizeTask(
-                                        mTask.mTaskId, mWindowDragBounds, RESIZE_MODE_USER);
-                            } catch (RemoteException e) {
-                            }
+                            mService.mAtmService.resizeTask(
+                                    mTask.mTaskId, mWindowDragBounds, RESIZE_MODE_USER);
                             Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
                         }
                     } break;
@@ -181,14 +174,12 @@
                         endDragLocked();
                         mTask.getDimBounds(mTmpRect);
                     }
-                    try {
-                        if (wasResizing && !mTmpRect.equals(mWindowDragBounds)) {
-                            // We were using fullscreen surface during resizing. Request
-                            // resizeTask() one last time to restore surface to window size.
-                            mActivityManager.resizeTask(
-                                    mTask.mTaskId, mWindowDragBounds, RESIZE_MODE_USER_FORCED);
-                        }
-                    } catch(RemoteException e) {}
+                    if (wasResizing && !mTmpRect.equals(mWindowDragBounds)) {
+                        // We were using fullscreen surface during resizing. Request
+                        // resizeTask() one last time to restore surface to window size.
+                        mService.mAtmService.resizeTask(
+                                mTask.mTaskId, mWindowDragBounds, RESIZE_MODE_USER_FORCED);
+                    }
 
                     // Post back to WM to handle clean-ups. We still need the input
                     // event handler for the last finishInputEvent()!
@@ -203,15 +194,10 @@
         }
     }
 
+    /** Use {@link #create(WindowManagerService)} instead. */
     @VisibleForTesting
-    TaskPositioner(WindowManagerService service, IActivityTaskManager activityManager) {
-        mService = service;
-        mActivityManager = activityManager;
-    }
-
-    /** Use {@link #create(WindowManagerService)} instead **/
     TaskPositioner(WindowManagerService service) {
-        this(service, service.mActivityTaskManager);
+        mService = service;
     }
 
     @VisibleForTesting
@@ -224,8 +210,6 @@
      * @param win The window which will be dragged.
      */
     void register(DisplayContent displayContent, @NonNull WindowState win) {
-        final Display display = displayContent.getDisplay();
-
         if (DEBUG_TASK_POSITIONING) {
             Slog.d(TAG, "Registering task positioner");
         }
@@ -236,7 +220,6 @@
         }
 
         mDisplayContent = displayContent;
-        display.getMetrics(mDisplayMetrics);
         final InputChannel[] channels = InputChannel.openInputChannelPair(TAG);
         mServerChannel = channels[0];
         mClientChannel = channels[1];
@@ -251,7 +234,8 @@
         mDragApplicationHandle.dispatchingTimeoutNanos =
                 WindowManagerService.DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS;
 
-        mDragWindowHandle = new InputWindowHandle(mDragApplicationHandle, display.getDisplayId());
+        mDragWindowHandle = new InputWindowHandle(mDragApplicationHandle,
+                displayContent.getDisplayId());
         mDragWindowHandle.name = TAG;
         mDragWindowHandle.token = mServerChannel.getToken();
         mDragWindowHandle.layoutParamsFlags = 0;
@@ -271,13 +255,13 @@
         // The drag window cannot receive new touches.
         mDragWindowHandle.touchableRegion.setEmpty();
 
-        // The drag window covers the entire display
-        mDragWindowHandle.frameLeft = 0;
-        mDragWindowHandle.frameTop = 0;
-        final Point p = new Point();
-        display.getRealSize(p);
-        mDragWindowHandle.frameRight = p.x;
-        mDragWindowHandle.frameBottom = p.y;
+        // The drag window covers the entire display.
+        final Rect displayBounds = mTmpRect;
+        displayContent.getBounds(mTmpRect);
+        mDragWindowHandle.frameLeft = displayBounds.left;
+        mDragWindowHandle.frameTop = displayBounds.top;
+        mDragWindowHandle.frameRight = displayBounds.right;
+        mDragWindowHandle.frameBottom = displayBounds.bottom;
 
         // Pause rotations before a drag.
         ProtoLog.d(WM_DEBUG_ORIENTATION, "Pausing rotation during re-position");
@@ -287,9 +271,10 @@
         mDisplayContent.getInputMonitor().updateInputWindowsImmediately();
         new SurfaceControl.Transaction().syncInputWindows().apply(true);
 
-        mMinVisibleWidth = dipToPixel(MINIMUM_VISIBLE_WIDTH_IN_DP, mDisplayMetrics);
-        mMinVisibleHeight = dipToPixel(MINIMUM_VISIBLE_HEIGHT_IN_DP, mDisplayMetrics);
-        display.getRealSize(mMaxVisibleSize);
+        final DisplayMetrics displayMetrics = displayContent.getDisplayMetrics();
+        mMinVisibleWidth = dipToPixel(MINIMUM_VISIBLE_WIDTH_IN_DP, displayMetrics);
+        mMinVisibleHeight = dipToPixel(MINIMUM_VISIBLE_HEIGHT_IN_DP, displayMetrics);
+        mMaxVisibleSize.set(displayBounds.width(), displayBounds.height());
 
         mDragEnded = false;
 
@@ -341,8 +326,11 @@
         mWindow = null;
     }
 
-    void startDrag(boolean resize, boolean preserveOrientation, float startX,
-            float startY) {
+    /**
+     * Starts moving or resizing the task. This method should be only called from
+     * {@link TaskPositioningController#startPositioningLocked} or unit tests.
+     */
+    void startDrag(boolean resize, boolean preserveOrientation, float startX, float startY) {
         if (DEBUG_TASK_POSITIONING) {
             Slog.d(TAG, "startDrag: win=" + mWindow + ", resize=" + resize
                     + ", preserveOrientation=" + preserveOrientation + ", {" + startX + ", "
@@ -351,12 +339,9 @@
         // Use the bounds of the task which accounts for
         // multiple app windows. Don't use any bounds from win itself as it
         // may not be the same size as the task.
-        mTask.getBounds(mTmpRect);
-        startDrag(resize, preserveOrientation, startX, startY, mTmpRect);
-    }
+        final Rect startBounds = mTmpRect;
+        mTask.getBounds(startBounds);
 
-    protected void startDrag(boolean resize, boolean preserveOrientation,
-                   float startX, float startY, Rect startBounds) {
         mCtrlType = CTRL_NONE;
         mStartDragX = startX;
         mStartDragY = startY;
@@ -389,20 +374,13 @@
         // bounds yet. This will guarantee that the app starts the backdrop renderer before
         // configuration changes which could cause an activity restart.
         if (mResizing) {
-            synchronized (mService.mGlobalLock) {
-                notifyMoveLocked(startX, startY);
-            }
+            notifyMoveLocked(startX, startY);
 
-            // Perform the resize on the WMS handler thread when we don't have the WMS lock held
-            // to ensure that we don't deadlock WMS and AMS. Note that WindowPositionerEventReceiver
-            // callbacks are delivered on the same handler so this initial resize is always
-            // guaranteed to happen before subsequent drag resizes.
+            // The WindowPositionerEventReceiver callbacks are delivered on the same handler so this
+            // initial resize is always guaranteed to happen before subsequent drag resizes.
             mService.mH.post(() -> {
-                try {
-                    mActivityManager.resizeTask(
-                            mTask.mTaskId, startBounds, RESIZE_MODE_USER_FORCED);
-                } catch (RemoteException e) {
-                }
+                mService.mAtmService.resizeTask(
+                        mTask.mTaskId, startBounds, RESIZE_MODE_USER_FORCED);
             });
         }
 
@@ -417,7 +395,8 @@
     }
 
     /** Returns true if the move operation should be ended. */
-    private boolean notifyMoveLocked(float x, float y) {
+    @VisibleForTesting
+    boolean notifyMoveLocked(float x, float y) {
         if (DEBUG_TASK_POSITIONING) {
             Slog.d(TAG, "notifyMoveLocked: {" + x + "," + y + "}");
         }
@@ -429,12 +408,11 @@
         }
 
         // This is a moving or scrolling operation.
-        mTask.getStack().getDimBounds(mTmpRect);
-        // If a target window is covered by system bar, there is no way to move it again by touch.
-        // So we exclude them from stack bounds. and then it will be shown inside stable area.
-        Rect stableBounds = new Rect();
-        mDisplayContent.getStableRect(stableBounds);
-        mTmpRect.intersect(stableBounds);
+        // Only allow to move in stable area so the target window won't be covered by system bar.
+        // Though {@link Task#resolveOverrideConfiguration} should also avoid the case.
+        mDisplayContent.getStableRect(mTmpRect);
+        // The task may be put in a limited display area.
+        mTmpRect.intersect(mTask.getRootTask().getParent().getBounds());
 
         int nX = (int) x;
         int nY = (int) y;
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
index 1796d85..4634e2d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
@@ -234,6 +234,10 @@
                 mTask = new TaskBuilder(mService.mStackSupervisor)
                         .setComponent(mComponent)
                         .setStack(mStack).build();
+            } else if (mTask == null && mStack != null && DisplayContent.alwaysCreateStack(
+                    mStack.getWindowingMode(), mStack.getActivityType())) {
+                // The stack can be the task root.
+                mTask = mStack;
             }
 
             Intent intent = new Intent();
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskPositionerTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskPositionerTests.java
index 52b465f..ea52d7d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskPositionerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskPositionerTests.java
@@ -36,7 +36,6 @@
 import android.platform.test.annotations.Presubmit;
 import android.util.DisplayMetrics;
 import android.util.Log;
-import android.view.Display;
 
 import androidx.test.filters.FlakyTest;
 import androidx.test.filters.SmallTest;
@@ -71,18 +70,21 @@
     public void setUp() {
         TaskPositioner.setFactory(null);
 
-        final Display display = mDisplayContent.getDisplay();
-        final DisplayMetrics dm = new DisplayMetrics();
-        display.getMetrics(dm);
+        final DisplayMetrics dm = mDisplayContent.getDisplayMetrics();
 
         // This should be the same calculation as the TaskPositioner uses.
         mMinVisibleWidth = dipToPixel(MINIMUM_VISIBLE_WIDTH_IN_DP, dm);
         mMinVisibleHeight = dipToPixel(MINIMUM_VISIBLE_HEIGHT_IN_DP, dm);
         removeGlobalMinSizeRestriction();
 
-        WindowState win = createWindow(null, TYPE_BASE_APPLICATION, "window");
-        mPositioner = new TaskPositioner(mWm, mWm.mAtmService);
-
+        final ActivityStack stack = createTaskStackOnDisplay(mDisplayContent);
+        final ActivityRecord activity = new ActivityTestsBase.ActivityBuilder(stack.mAtmService)
+                .setStack(stack)
+                // In real case, there is no additional level for freeform mode.
+                .setCreateTask(false)
+                .build();
+        final WindowState win = createWindow(null, TYPE_BASE_APPLICATION, activity, "window");
+        mPositioner = new TaskPositioner(mWm);
         mPositioner.register(mDisplayContent, win);
 
         win.getRootTask().setWindowingMode(WINDOWING_MODE_FREEFORM);
@@ -109,6 +111,28 @@
         assertTrue(created[0]);
     }
 
+    /** This tests that the window can move in all directions. */
+    @Test
+    public void testMoveWindow() {
+        final Rect displayBounds = mDisplayContent.getBounds();
+        final int windowSize = Math.min(displayBounds.width(), displayBounds.height()) / 2;
+        final int left = displayBounds.centerX() - windowSize / 2;
+        final int top = displayBounds.centerY() - windowSize / 2;
+        final Rect r = new Rect(left, top, left + windowSize, top + windowSize);
+        mPositioner.mTask.setBounds(r);
+        mPositioner.startDrag(false /* resizing */, false /* preserveOrientation */, left, top);
+
+        // Move upper left.
+        mPositioner.notifyMoveLocked(left - MOUSE_DELTA_X, top - MOUSE_DELTA_Y);
+        r.offset(-MOUSE_DELTA_X, -MOUSE_DELTA_Y);
+        assertBoundsEquals(r, mPositioner.getWindowDragBounds());
+
+        // Move bottom right.
+        mPositioner.notifyMoveLocked(left, top);
+        r.offset(MOUSE_DELTA_X, MOUSE_DELTA_Y);
+        assertBoundsEquals(r, mPositioner.getWindowDragBounds());
+    }
+
     /**
      * This tests that free resizing will allow to change the orientation as well
      * as does some basic tests (e.g. dragging in Y only will keep X stable).