Make DragDropController#mDragState private

Before introducing the drag and drop callback, we need a write lock to preserve
the order of drag state update.

The CL changes the visibility of mDragState to private so that we can
ensure the upcoming write lock guards all updates for mDragState.

Bug: 65564090
Test: android.server.wm.CrossAppDragAndDropTests, manually check the
      drag and drop behavior on test app.
Change-Id: I3afc06221e4a9688b417768365e87b19706f9372
diff --git a/services/core/java/com/android/server/wm/DragDropController.java b/services/core/java/com/android/server/wm/DragDropController.java
index b6f0f6a..7090833 100644
--- a/services/core/java/com/android/server/wm/DragDropController.java
+++ b/services/core/java/com/android/server/wm/DragDropController.java
@@ -35,6 +35,7 @@
 import android.view.SurfaceControl;
 import android.view.SurfaceSession;
 import android.view.View;
+import com.android.server.input.InputWindowHandle;
 
 /**
  * Managing drag and drop operations initiated by View#startDragAndDrop.
@@ -54,7 +55,7 @@
      * The variable is cleared by {@code #onDragStateClosedLocked} which is invoked by DragState
      * itself, thus the variable can be null after calling DragState's methods.
      */
-    DragState mDragState;
+    private DragState mDragState;
 
     private WindowManagerService mService;
     private final Handler mHandler;
@@ -63,11 +64,19 @@
         return mDragState != null;
     }
 
+    InputWindowHandle getInputWindowHandleLocked() {
+        return mDragState.getInputWindowHandle();
+    }
+
     DragDropController(WindowManagerService service, Looper looper) {
         mService = service;
         mHandler = new DragHandler(service, looper);
     }
 
+    void sendDragStartedIfNeededLocked(WindowState window) {
+        mDragState.sendDragStartedIfNeededLocked(window);
+    }
+
     IBinder prepareDrag(SurfaceSession session, int callerPid,
             int callerUid, IWindow window, int flags, int width, int height, Surface outSurface) {
         if (DEBUG_DRAG) {
@@ -257,6 +266,30 @@
         }
     }
 
+    /**
+     * Handles motion events.
+     * @param keepHandling Whether if the drag operation is continuing or this is the last motion
+     *          event.
+     * @param newX X coordinate value in dp in the screen coordinate
+     * @param newY Y coordinate value in dp in the screen coordinate
+     */
+    void handleMotionEvent(boolean keepHandling, float newX, float newY) {
+        synchronized (mService.mWindowMap) {
+            if (!dragDropActiveLocked()) {
+                // The drag has ended but the clean-up message has not been processed by
+                // window manager. Drop events that occur after this until window manager
+                // has a chance to clean-up the input handle.
+                return;
+            }
+
+            if (keepHandling) {
+                mDragState.notifyMoveLocked(newX, newY);
+            } else {
+                mDragState.notifyDropLocked(newX, newY);
+            }
+        }
+    }
+
     void dragRecipientEntered(IWindow window) {
         if (DEBUG_DRAG) {
             Slog.d(TAG_WM, "Drag into new candidate view @ " + window.asBinder());
diff --git a/services/core/java/com/android/server/wm/DragInputEventReceiver.java b/services/core/java/com/android/server/wm/DragInputEventReceiver.java
index b4bbc90..bee2bac 100644
--- a/services/core/java/com/android/server/wm/DragInputEventReceiver.java
+++ b/services/core/java/com/android/server/wm/DragInputEventReceiver.java
@@ -37,7 +37,6 @@
  * Input receiver for drag and drop
  */
 class DragInputEventReceiver extends InputEventReceiver {
-    private final WindowManagerService mService;
     private final DragDropController mDragDropController;
 
     // Set, if stylus button was down at the start of the drag.
@@ -48,100 +47,63 @@
     // are still being dispatched.
     private boolean mMuteInput = false;
 
-    public DragInputEventReceiver(InputChannel inputChannel, Looper looper,
-            DragDropController controller, WindowManagerService service) {
+    DragInputEventReceiver(InputChannel inputChannel, Looper looper,
+            DragDropController controller) {
         super(inputChannel, looper);
         mDragDropController = controller;
-        mService = service;
     }
 
     @Override
     public void onInputEvent(InputEvent event, int displayId) {
         boolean handled = false;
         try {
-            synchronized (mService.mWindowMap) {
-                if (!mDragDropController.dragDropActiveLocked()) {
-                    // The drag has ended but the clean-up message has not been processed by
-                    // window manager. Drop events that occur after this until window manager
-                    // has a chance to clean-up the input handle.
-                    handled = true;
-                    return;
-                }
-                if (!(event instanceof MotionEvent)
-                        || (event.getSource() & SOURCE_CLASS_POINTER) == 0
-                        || mMuteInput) {
-                    return;
-                }
-                final MotionEvent motionEvent = (MotionEvent) event;
-                boolean endDrag = false;
-                final float newX = motionEvent.getRawX();
-                final float newY = motionEvent.getRawY();
-                final boolean isStylusButtonDown =
-                        (motionEvent.getButtonState() & BUTTON_STYLUS_PRIMARY) != 0;
-
-                if (mIsStartEvent) {
-                    if (isStylusButtonDown) {
-                        // First event and the button was down, check for the button being
-                        // lifted in the future, if that happens we'll drop the item.
-                        mStylusButtonDownAtStart = true;
-                    }
-                    mIsStartEvent = false;
-                }
-
-                switch (motionEvent.getAction()) {
-                    case ACTION_DOWN: {
-                        if (DEBUG_DRAG) Slog.w(TAG_WM, "Unexpected ACTION_DOWN in drag layer");
-                    }
-                    break;
-
-                    case ACTION_MOVE: {
-                        if (mStylusButtonDownAtStart && !isStylusButtonDown) {
-                            if (DEBUG_DRAG) {
-                                Slog.d(TAG_WM, "Button no longer pressed; dropping at "
-                                        + newX + "," + newY);
-                            }
-                            mMuteInput = true;
-                            endDrag = mDragDropController.mDragState
-                                    .notifyDropLocked(newX, newY);
-                        } else {
-                            // move the surface and tell the involved window(s) where we are
-                            mDragDropController.mDragState.notifyMoveLocked(newX, newY);
-                        }
-                    }
-                    break;
-
-                    case ACTION_UP: {
-                        if (DEBUG_DRAG) {
-                            Slog.d(TAG_WM, "Got UP on move channel; dropping at "
-                                    + newX + "," + newY);
-                        }
-                        mMuteInput = true;
-                        endDrag = mDragDropController.mDragState
-                                .notifyDropLocked(newX, newY);
-                    }
-                    break;
-
-                    case ACTION_CANCEL: {
-                        if (DEBUG_DRAG) Slog.d(TAG_WM, "Drag cancelled!");
-                        mMuteInput = true;
-                        endDrag = true;
-                    }
-                    break;
-                }
-
-                if (endDrag) {
-                    if (DEBUG_DRAG)
-                        Slog.d(TAG_WM, "Drag ended; tearing down state");
-                    // tell all the windows that the drag has ended
-                    // endDragLocked will post back to looper to dispose the receiver
-                    // since we still need the receiver for the last finishInputEvent.
-                    mDragDropController.mDragState.endDragLocked();
-                    mStylusButtonDownAtStart = false;
-                    mIsStartEvent = true;
-                }
-
-                handled = true;
+            if (!(event instanceof MotionEvent)
+                    || (event.getSource() & SOURCE_CLASS_POINTER) == 0
+                    || mMuteInput) {
+                return;
             }
+            final MotionEvent motionEvent = (MotionEvent) event;
+            final float newX = motionEvent.getRawX();
+            final float newY = motionEvent.getRawY();
+            final boolean isStylusButtonDown =
+                    (motionEvent.getButtonState() & BUTTON_STYLUS_PRIMARY) != 0;
+
+            if (mIsStartEvent) {
+                // First event and the button was down, check for the button being
+                // lifted in the future, if that happens we'll drop the item.
+                mStylusButtonDownAtStart = isStylusButtonDown;
+                mIsStartEvent = false;
+            }
+
+            switch (motionEvent.getAction()) {
+                case ACTION_DOWN:
+                    if (DEBUG_DRAG) Slog.w(TAG_WM, "Unexpected ACTION_DOWN in drag layer");
+                    return;
+                case ACTION_MOVE:
+                    if (mStylusButtonDownAtStart && !isStylusButtonDown) {
+                        if (DEBUG_DRAG) {
+                            Slog.d(TAG_WM, "Button no longer pressed; dropping at " + newX + ","
+                                    + newY);
+                        }
+                        mMuteInput = true;
+                    }
+                    break;
+                case ACTION_UP:
+                    if (DEBUG_DRAG) {
+                        Slog.d(TAG_WM, "Got UP on move channel; dropping at " + newX + "," + newY);
+                    }
+                    mMuteInput = true;
+                    break;
+                case ACTION_CANCEL:
+                    if (DEBUG_DRAG) Slog.d(TAG_WM, "Drag cancelled!");
+                    mMuteInput = true;
+                    break;
+                default:
+                    return;
+            }
+
+            mDragDropController.handleMotionEvent(!mMuteInput /* keepHandling */, newX, newY);
+            handled = true;
         } catch (Exception e) {
             Slog.e(TAG_WM, "Exception caught by drag handleMotion", e);
         } finally {
diff --git a/services/core/java/com/android/server/wm/DragState.java b/services/core/java/com/android/server/wm/DragState.java
index da0394b..0727107 100644
--- a/services/core/java/com/android/server/wm/DragState.java
+++ b/services/core/java/com/android/server/wm/DragState.java
@@ -214,7 +214,7 @@
             mClientChannel = channels[1];
             mService.mInputManager.registerInputChannel(mServerChannel, null);
             mInputEventReceiver = new DragInputEventReceiver(mClientChannel,
-                    mService.mH.getLooper(), mDragDropController, mService);
+                    mService.mH.getLooper(), mDragDropController);
 
             mDragApplicationHandle = new InputApplicationHandle(null);
             mDragApplicationHandle.name = "drag";
@@ -508,34 +508,33 @@
         mTargetWindow = touchedWin;
     }
 
-    // Find the drop target and tell it about the data.  Returns 'true' if we can immediately
-    // dispatch the global drag-ended message, 'false' if we need to wait for a
-    // result from the recipient.
-    boolean notifyDropLocked(float x, float y) {
+    /**
+     * Finds the drop target and tells it about the data. If the drop event is not sent to the
+     * target, invokes {@code endDragLocked} immediately.
+     */
+    void notifyDropLocked(float x, float y) {
         if (mAnimator != null) {
-            return false;
+            return;
         }
         mCurrentX = x;
         mCurrentY = y;
 
-        WindowState touchedWin = mDisplayContent.getTouchableWinAtPointLocked(x, y);
+        final WindowState touchedWin = mDisplayContent.getTouchableWinAtPointLocked(x, y);
 
         if (!isWindowNotified(touchedWin)) {
             // "drop" outside a valid window -- no recipient to apply a
             // timeout to, and we can send the drag-ended message immediately.
             mDragResult = false;
-            return true;
+            endDragLocked();
+            return;
         }
 
-        if (DEBUG_DRAG) {
-            Slog.d(TAG_WM, "sending DROP to " + touchedWin);
-        }
+        if (DEBUG_DRAG) Slog.d(TAG_WM, "sending DROP to " + touchedWin);
 
         final int targetUserId = UserHandle.getUserId(touchedWin.getOwningUid());
 
-        DragAndDropPermissionsHandler dragAndDropPermissions = null;
-        if ((mFlags & View.DRAG_FLAG_GLOBAL) != 0 &&
-                (mFlags & DRAG_FLAGS_URI_ACCESS) != 0) {
+        final DragAndDropPermissionsHandler dragAndDropPermissions;
+        if ((mFlags & View.DRAG_FLAG_GLOBAL) != 0 && (mFlags & DRAG_FLAGS_URI_ACCESS) != 0) {
             dragAndDropPermissions = new DragAndDropPermissionsHandler(
                     mData,
                     mUid,
@@ -543,13 +542,15 @@
                     mFlags & DRAG_FLAGS_URI_PERMISSIONS,
                     mSourceUserId,
                     targetUserId);
+        } else {
+            dragAndDropPermissions = null;
         }
         if (mSourceUserId != targetUserId){
             mData.fixUris(mSourceUserId);
         }
         final int myPid = Process.myPid();
         final IBinder token = touchedWin.mClient.asBinder();
-        DragEvent evt = obtainDragEvent(touchedWin, DragEvent.ACTION_DROP, x, y,
+        final DragEvent evt = obtainDragEvent(touchedWin, DragEvent.ACTION_DROP, x, y,
                 null, null, mData, dragAndDropPermissions, false);
         try {
             touchedWin.mClient.dispatchDragEvent(evt);
@@ -558,14 +559,13 @@
             mDragDropController.sendTimeoutMessage(MSG_DRAG_END_TIMEOUT, token);
         } catch (RemoteException e) {
             Slog.w(TAG_WM, "can't send drop notification to win " + touchedWin);
-            return true;
+            endDragLocked();
         } finally {
             if (myPid != touchedWin.mSession.mPid) {
                 evt.recycle();
             }
         }
         mToken = token;
-        return false;
     }
 
     private static DragEvent obtainDragEvent(WindowState win, int action,
diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java
index d23369e..a4080ca 100644
--- a/services/core/java/com/android/server/wm/InputMonitor.java
+++ b/services/core/java/com/android/server/wm/InputMonitor.java
@@ -373,7 +373,7 @@
                 Log.d(TAG_WM, "Inserting drag window");
             }
             final InputWindowHandle dragWindowHandle =
-                    mService.mDragDropController.mDragState.getInputWindowHandle();
+                    mService.mDragDropController.getInputWindowHandleLocked();
             if (dragWindowHandle != null) {
                 addInputWindowHandle(dragWindowHandle);
             } else {
@@ -690,7 +690,7 @@
             // If there's a drag in progress and 'child' is a potential drop target,
             // make sure it's been told about the drag
             if (inDrag && isVisible && w.getDisplayContent().isDefaultDisplay) {
-                mService.mDragDropController.mDragState.sendDragStartedIfNeededLocked(w);
+                mService.mDragDropController.sendDragStartedIfNeededLocked(w);
             }
 
             addInputWindowHandle(
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index c982d15..ac2977c 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -7054,7 +7054,7 @@
         }
 
         synchronized (mWindowMap) {
-            if (mDragDropController.mDragState != null) {
+            if (mDragDropController.dragDropActiveLocked()) {
                 // Drag cursor overrides the app cursor.
                 return;
             }