From 768012e4c18dd8650bf1b067f7e0b37a3c5db462 Mon Sep 17 00:00:00 2001 From: Daichi Hirono Date: Mon, 30 Oct 2017 10:05:37 +0900 Subject: [PATCH] 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 --- .../android/server/wm/DragDropController.java | 35 ++++- .../server/wm/DragInputEventReceiver.java | 134 +++++++----------- .../java/com/android/server/wm/DragState.java | 34 ++--- .../com/android/server/wm/InputMonitor.java | 4 +- .../server/wm/WindowManagerService.java | 2 +- 5 files changed, 102 insertions(+), 107 deletions(-) diff --git a/services/core/java/com/android/server/wm/DragDropController.java b/services/core/java/com/android/server/wm/DragDropController.java index b6f0f6a134543..70908337dfd38 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.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()); diff --git a/services/core/java/com/android/server/wm/DragInputEventReceiver.java b/services/core/java/com/android/server/wm/DragInputEventReceiver.java index b4bbc9055631d..bee2bacf83d2e 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 @@ 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; - } - 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 da0394b6e294d..0727107cd4528 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 @@ 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, diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java index d23369efd2971..a4080cacf4305 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 @@ 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( diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index c982d1576d466..ac2977c58f794 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 @@ public class WindowManagerService extends IWindowManager.Stub } synchronized (mWindowMap) { - if (mDragDropController.mDragState != null) { + if (mDragDropController.dragDropActiveLocked()) { // Drag cursor overrides the app cursor. return; }