diff options
5 files changed, 187 insertions, 187 deletions
diff --git a/services/core/java/com/android/server/wm/DragDropController.java b/services/core/java/com/android/server/wm/DragDropController.java index 79d46ce51326..cc4e0f8a5004 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. @@ -49,7 +50,12 @@ class DragDropController { static final int MSG_TEAR_DOWN_DRAG_AND_DROP_INPUT = 2; static final int MSG_ANIMATION_END = 3; - DragState mDragState; + /** + * Drag state per operation. + * The variable is cleared by {@code #onDragStateClosedLocked} which is invoked by DragState + * itself, thus the variable can be null after calling DragState's methods. + */ + private DragState mDragState; private WindowManagerService mService; private final Handler mHandler; @@ -58,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) { @@ -158,9 +172,7 @@ class DragDropController { if (!mService.mInputManager.transferTouchFocus(callingWin.mInputChannel, mDragState.getInputChannel())) { Slog.e(TAG_WM, "Unable to transfer touch focus"); - mDragState.unregister(); - mDragState.reset(); - mDragState = null; + mDragState.closeLocked(); return false; } @@ -254,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()); @@ -282,6 +318,17 @@ class DragDropController { mHandler.sendMessageDelayed(msg, DRAG_TIMEOUT_MS); } + /** + * Notifies the current drag state is closed. + */ + void onDragStateClosedLocked(DragState dragState) { + if (mDragState != dragState) { + Slog.wtf(TAG_WM, "Unknown drag state is closed"); + return; + } + mDragState = null; + } + private class DragHandler extends Handler { /** * Lock for window manager. @@ -304,9 +351,7 @@ class DragDropController { synchronized (mService.mWindowMap) { // !!! TODO: ANR the app that has failed to start the drag in time if (mDragState != null) { - mDragState.unregister(); - mDragState.reset(); - mDragState = null; + mDragState.closeLocked(); } } break; @@ -346,7 +391,7 @@ class DragDropController { "plyaing animation"); return; } - mDragState.onAnimationEndLocked(); + mDragState.closeLocked(); } break; } diff --git a/services/core/java/com/android/server/wm/DragInputEventReceiver.java b/services/core/java/com/android/server/wm/DragInputEventReceiver.java index b4bbc9055631..bee2bacf83d2 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; + 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; - 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; + 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; + } - case ACTION_UP: { + 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, "Got UP on move channel; dropping at " - + newX + "," + newY); + Slog.d(TAG_WM, "Button no longer pressed; 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; + case ACTION_UP: + if (DEBUG_DRAG) { + Slog.d(TAG_WM, "Got UP on move channel; dropping at " + newX + "," + newY); } + mMuteInput = 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; + 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 861fb443b6d0..e81d366bd85a 100644 --- a/services/core/java/com/android/server/wm/DragState.java +++ b/services/core/java/com/android/server/wm/DragState.java @@ -105,6 +105,11 @@ class DragState { WindowState mTargetWindow; ArrayList<WindowState> mNotifiedWindows; boolean mDragInProgress; + /** + * Whether if animation is completed. Needs to be volatile to update from the animation thread + * without having a WM lock. + */ + volatile boolean mAnimationCompleted = false; DisplayContent mDisplayContent; @Nullable private ValueAnimator mAnimator; @@ -122,21 +127,79 @@ class DragState { mNotifiedWindows = new ArrayList<WindowState>(); } - void reset() { - if (mAnimator != null) { - Slog.wtf(TAG_WM, - "Unexpectedly destroying mSurfaceControl while animation is running"); + /** + * After calling this, DragDropController#onDragStateClosedLocked is invoked, which causes + * DragDropController#mDragState becomes null. + */ + void closeLocked() { + // Unregister the input interceptor. + if (mInputInterceptor != null) { + if (DEBUG_DRAG) + Slog.d(TAG_WM, "unregistering drag input channel"); + + // Input channel should be disposed on the thread where the input is being handled. + mDragDropController.sendHandlerMessage( + MSG_TEAR_DOWN_DRAG_AND_DROP_INPUT, mInputInterceptor); + mInputInterceptor = null; + mService.mInputMonitor.updateInputWindowsLw(true /*force*/); + } + + // Send drag end broadcast if drag start has been sent. + if (mDragInProgress) { + final int myPid = Process.myPid(); + + if (DEBUG_DRAG) { + Slog.d(TAG_WM, "broadcasting DRAG_ENDED"); + } + for (WindowState ws : mNotifiedWindows) { + float x = 0; + float y = 0; + if (!mDragResult && (ws.mSession.mPid == mPid)) { + // Report unconsumed drop location back to the app that started the drag. + x = mCurrentX; + y = mCurrentY; + } + DragEvent evt = DragEvent.obtain(DragEvent.ACTION_DRAG_ENDED, + x, y, null, null, null, null, mDragResult); + try { + ws.mClient.dispatchDragEvent(evt); + } catch (RemoteException e) { + Slog.w(TAG_WM, "Unable to drag-end window " + ws); + } + // if the current window is in the same process, + // the dispatch has already recycled the event + if (myPid != ws.mSession.mPid) { + evt.recycle(); + } + } + mNotifiedWindows.clear(); + mDragInProgress = false; + } + + // Take the cursor back if it has been changed. + if (isFromSource(InputDevice.SOURCE_MOUSE)) { + mService.restorePointerIconLocked(mDisplayContent, mCurrentX, mCurrentY); + mTouchSource = 0; } + + // Clear the internal variables. if (mSurfaceControl != null) { mSurfaceControl.destroy(); + mSurfaceControl = null; + } + if (mAnimator != null && !mAnimationCompleted) { + Slog.wtf(TAG_WM, + "Unexpectedly destroying mSurfaceControl while animation is running"); } - mSurfaceControl = null; mFlags = 0; mLocalWin = null; mToken = null; mData = null; mThumbOffsetX = mThumbOffsetY = 0; mNotifiedWindows = null; + + // Notifies the controller that the drag state is closed. + mDragDropController.onDragStateClosedLocked(this); } class InputInterceptor { @@ -151,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"; @@ -235,19 +298,6 @@ class DragState { } } - void unregister() { - if (DEBUG_DRAG) Slog.d(TAG_WM, "unregistering drag input channel"); - if (mInputInterceptor == null) { - Slog.e(TAG_WM, "Unregister of nonexistent drag input channel"); - } else { - // Input channel should be disposed on the thread where the input is being handled. - mDragDropController.sendHandlerMessage( - MSG_TEAR_DOWN_DRAG_AND_DROP_INPUT, mInputInterceptor); - mInputInterceptor = null; - mService.mInputMonitor.updateInputWindowsLw(true /*force*/); - } - } - int getDragLayerLocked() { return mService.mPolicy.getWindowLayerFromTypeLw(WindowManager.LayoutParams.TYPE_DRAG) * WindowManagerService.TYPE_LAYER_MULTIPLIER @@ -366,46 +416,15 @@ class DragState { return false; } - private void broadcastDragEndedLocked() { - final int myPid = Process.myPid(); - - if (DEBUG_DRAG) { - Slog.d(TAG_WM, "broadcasting DRAG_ENDED"); - } - for (WindowState ws : mNotifiedWindows) { - float x = 0; - float y = 0; - if (!mDragResult && (ws.mSession.mPid == mPid)) { - // Report unconsumed drop location back to the app that started the drag. - x = mCurrentX; - y = mCurrentY; - } - DragEvent evt = DragEvent.obtain(DragEvent.ACTION_DRAG_ENDED, - x, y, null, null, null, null, mDragResult); - try { - ws.mClient.dispatchDragEvent(evt); - } catch (RemoteException e) { - Slog.w(TAG_WM, "Unable to drag-end window " + ws); - } - // if the current window is in the same process, - // the dispatch has already recycled the event - if (myPid != ws.mSession.mPid) { - evt.recycle(); - } - } - mNotifiedWindows.clear(); - mDragInProgress = false; - } - void endDragLocked() { if (mAnimator != null) { return; } if (!mDragResult) { mAnimator = createReturnAnimationLocked(); - return; // Will call cleanUpDragLw when the animation is done. + return; // Will call closeLocked() when the animation is done. } - cleanUpDragLocked(); + closeLocked(); } void cancelDragLocked() { @@ -414,33 +433,15 @@ class DragState { } if (!mDragInProgress) { // This can happen if an app invokes Session#cancelDragAndDrop before - // Session#performDrag. Reset the drag state: - // 1. without sending the end broadcast because the start broadcast has not been sent, - // and - // 2. without playing the cancel animation because H.DRAG_START_TIMEOUT may be sent to - // WindowManagerService, which will cause DragState#reset() while playing the - // cancel animation. - reset(); - mDragDropController.mDragState = null; + // Session#performDrag. Reset the drag state without playing the cancel animation + // because H.DRAG_START_TIMEOUT may be sent to WindowManagerService, which will cause + // DragState#reset() while playing the cancel animation. + closeLocked(); return; } mAnimator = createCancelAnimationLocked(); } - private void cleanUpDragLocked() { - broadcastDragEndedLocked(); - if (isFromSource(InputDevice.SOURCE_MOUSE)) { - mService.restorePointerIconLocked(mDisplayContent, mCurrentX, mCurrentY); - } - - // stop intercepting input - unregister(); - - // free our resources and drop all the object references - reset(); - mDragDropController.mDragState = null; - } - void notifyMoveLocked(float x, float y) { if (mAnimator != null) { return; @@ -507,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, @@ -542,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); @@ -557,23 +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; - } - - void onAnimationEndLocked() { - if (mAnimator == null) { - Slog.wtf(TAG_WM, "Unexpected null mAnimator"); - return; - } - mAnimator = null; - cleanUpDragLocked(); } private static DragEvent obtainDragEvent(WindowState win, int action, @@ -677,6 +669,7 @@ class DragState { @Override public void onAnimationEnd(Animator animator) { + mAnimationCompleted = true; // Updating mDragState requires the WM lock so continues it on the out of // AnimationThread. mDragDropController.sendHandlerMessage(MSG_ANIMATION_END, null); diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java index 40eab45a26d9..a766097c7078 100644 --- a/services/core/java/com/android/server/wm/InputMonitor.java +++ b/services/core/java/com/android/server/wm/InputMonitor.java @@ -381,7 +381,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 { @@ -698,7 +698,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 f71520e3c848..f313f317510b 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -7087,7 +7087,7 @@ public class WindowManagerService extends IWindowManager.Stub } synchronized (mWindowMap) { - if (mDragDropController.mDragState != null) { + if (mDragDropController.dragDropActiveLocked()) { // Drag cursor overrides the app cursor. return; } |