Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 768012e4 authored by Daichi Hirono's avatar Daichi Hirono
Browse files

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
parent 3bae0b02
Loading
Loading
Loading
Loading
+34 −1
Original line number Diff line number Diff line
@@ -35,6 +35,7 @@ import android.view.Surface;
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 @@ class DragDropController {
     * 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 @@ class DragDropController {
        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 @@ class DragDropController {
        }
    }

    /**
     * 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());
+39 −77
Original line number Diff line number Diff line
@@ -37,7 +37,6 @@ import android.view.MotionEvent;
 * 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 @@ class DragInputEventReceiver extends InputEventReceiver {
    // 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;
                    }
                mStylusButtonDownAtStart = isStylusButtonDown;
                mIsStartEvent = false;
            }

            switch (motionEvent.getAction()) {
                    case ACTION_DOWN: {
                case ACTION_DOWN:
                    if (DEBUG_DRAG) Slog.w(TAG_WM, "Unexpected ACTION_DOWN in drag layer");
                    }
                    break;

                    case ACTION_MOVE: {
                    return;
                case ACTION_MOVE:
                    if (mStylusButtonDownAtStart && !isStylusButtonDown) {
                        if (DEBUG_DRAG) {
                                Slog.d(TAG_WM, "Button no longer pressed; dropping at "
                                        + newX + "," + newY);
                            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: {
                case ACTION_UP:
                    if (DEBUG_DRAG) {
                            Slog.d(TAG_WM, "Got UP on move channel; dropping at "
                                    + newX + "," + newY);
                        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: {
                case ACTION_CANCEL:
                    if (DEBUG_DRAG) Slog.d(TAG_WM, "Drag cancelled!");
                    mMuteInput = true;
                        endDrag = true;
                    }
                    break;
                default:
                    return;
            }

                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;
                }

            mDragDropController.handleMotionEvent(!mMuteInput /* keepHandling */, newX, newY);
            handled = true;
            }
        } catch (Exception e) {
            Slog.e(TAG_WM, "Exception caught by drag handleMotion", e);
        } finally {
+17 −17
Original line number Diff line number Diff line
@@ -214,7 +214,7 @@ class DragState {
            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 @@ class DragState {
        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 @@ class DragState {
                    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 @@ class DragState {
            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,
+2 −2
Original line number Diff line number Diff line
@@ -373,7 +373,7 @@ final class InputMonitor implements InputManagerService.WindowManagerCallbacks {
                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 @@ final class InputMonitor implements InputManagerService.WindowManagerCallbacks {
            // 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(
+1 −1
Original line number Diff line number Diff line
@@ -7054,7 +7054,7 @@ public class WindowManagerService extends IWindowManager.Stub
        }

        synchronized (mWindowMap) {
            if (mDragDropController.mDragState != null) {
            if (mDragDropController.dragDropActiveLocked()) {
                // Drag cursor overrides the app cursor.
                return;
            }